samagotchi 0.151.0 → 0.152.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +29 -1
- data/docs/bundle-authoring.md +4 -4
- data/docs/cli.md +32 -7
- data/docs/guardrails.md +4 -0
- data/docs/internals/block-renderers.md +3 -1
- data/docs/internals/bundles.md +48 -14
- data/docs/internals/objectives.md +6 -4
- data/docs/memory.md +6 -1
- data/docs/plugins.md +51 -14
- data/docs/releasing.md +7 -2
- data/docs/sessions.md +1 -1
- data/lib/samagotchi/block_renderers.rb +9 -8
- data/lib/samagotchi/bundle_command/check.rb +43 -3
- data/lib/samagotchi/bundle_command/installed_check.rb +6 -2
- data/lib/samagotchi/bundle_command/try/sandbox.rb +11 -3
- data/lib/samagotchi/bundle_command.rb +88 -10
- data/lib/samagotchi/bundle_needs.rb +4 -6
- data/lib/samagotchi/bundle_settings_check.rb +14 -13
- data/lib/samagotchi/bundles/agents-md/manifest.yml +2 -2
- data/lib/samagotchi/bundles/btw/manifest.yml +2 -2
- data/lib/samagotchi/bundles/loop-guard/manifest.yml +2 -2
- data/lib/samagotchi/bundles/mcp/manifest.yml +2 -2
- data/lib/samagotchi/bundles/skills/manifest.yml +2 -2
- data/lib/samagotchi/bundles/slow-commands/manifest.yml +2 -2
- data/lib/samagotchi/bundles/system/manifest.yml +2 -2
- data/lib/samagotchi/bundles/system/memory_guide.md +3 -3
- data/lib/samagotchi/child_env.rb +15 -0
- data/lib/samagotchi/context_providers.rb +42 -10
- data/lib/samagotchi/engine.rb +27 -4
- data/lib/samagotchi/extension_load.rb +4 -3
- data/lib/samagotchi/guardrail_wiring.rb +28 -19
- data/lib/samagotchi/guardrails/approval.rb +2 -2
- data/lib/samagotchi/guardrails/rules.rb +1 -0
- data/lib/samagotchi/guardrails/verdict.rb +20 -0
- data/lib/samagotchi/hooks/bundle_loader.rb +5 -7
- data/lib/samagotchi/memory_bundle/asset_installer.rb +11 -8
- data/lib/samagotchi/memory_bundle/builder.rb +36 -30
- data/lib/samagotchi/memory_bundle/bundle_hook.rb +3 -2
- data/lib/samagotchi/memory_bundle/file_digest.rb +31 -0
- data/lib/samagotchi/memory_bundle/file_standing.rb +2 -2
- data/lib/samagotchi/memory_bundle/installed_bundle.rb +55 -46
- data/lib/samagotchi/memory_bundle/installed_copy.rb +38 -15
- data/lib/samagotchi/memory_bundle/installed_manifest.rb +224 -0
- data/lib/samagotchi/memory_bundle/installer.rb +90 -16
- data/lib/samagotchi/memory_bundle/manifest.rb +146 -6
- data/lib/samagotchi/memory_bundle/manifest_standing.rb +171 -0
- data/lib/samagotchi/memory_bundle/merger.rb +7 -8
- data/lib/samagotchi/memory_bundle/profile.rb +3 -2
- data/lib/samagotchi/memory_bundle/provenance.rb +217 -18
- data/lib/samagotchi/memory_bundle/shas.rb +4 -3
- data/lib/samagotchi/memory_bundle/shipped_update.rb +52 -24
- data/lib/samagotchi/memory_bundle/status.rb +11 -10
- data/lib/samagotchi/memory_bundle/system_bundle.rb +13 -2
- data/lib/samagotchi/plugin/loader.rb +1 -1
- data/lib/samagotchi/session_config.rb +5 -4
- data/lib/samagotchi/system_one/record.rb +10 -2
- data/lib/samagotchi/tools/delegate.rb +20 -2
- data/lib/samagotchi/update_command.rb +13 -2
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/web/app.rb +2 -1
- data/lib/samagotchi/web/session_access_routes.rb +2 -1
- data/lib/samagotchi/web/session_hub.rb +6 -0
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6d208a5aecb9745a21107ab97399c7ec4901c24fd781e3ba4ec1658a69764ded
|
|
4
|
+
data.tar.gz: abc21f8bfe39f0bdc614b70b755dd3cd195d016dd305b6d3ba28b018d0b75d54
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1105443bb7097fd93f0ab4bb1f0f9926fbbc9c89f9d57584d12ae3ac560b726498c607c1e897416e8a17f0e150076fc7de2b2b19b0919b49ad5b40645dd3883a
|
|
7
|
+
data.tar.gz: ba009b4669d67eb24ccd15cf669f84431a1738153b721c13c18633d6b05f32cdd4bfd01d25530b18ef5ab39cbf84e0b19688691f6751884c92e0a8d412935771
|
data/CHANGELOG.md
CHANGED
|
@@ -5,8 +5,35 @@ format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
|
|
|
5
5
|
versions follow [Semantic Versioning](https://semver.org/); before 1.0, config
|
|
6
6
|
and commands may change between minor versions. How releases are made:
|
|
7
7
|
[docs/releasing.md](docs/releasing.md).
|
|
8
|
+
|
|
8
9
|
## [Unreleased]
|
|
9
10
|
|
|
11
|
+
## [0.152.0] - 2026-11-06
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- `chi bundle install` and `chi bundle check` name a manifest key this chi doesn't read (`unknown-key`); `chi bundle check` also warns (`requires-chi-floor`) when a manifest uses a key newer than its `requires_chi` admits. [docs/plugins.md](docs/plugins.md)
|
|
16
|
+
- `chi bundle status NAME` has a `Manifest:` line saying where the bundle's manifest is read from, and why.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- An installed bundle keeps its `manifest.yml` and chi reads it live, so a newer chi reads keys the installing chi didn't know, with no reinstall. Bundles installed earlier get theirs on `chi update` (shipped bundles, or a folder or archive that is still there; the system bundle at the next start); for one from git, `chi update` names the `chi bundle upgrade NAME` to run. [docs/internals/bundles.md](docs/internals/bundles.md)
|
|
21
|
+
- `chi bundle install` and `chi bundle upgrade` refuse a bundle whose `requires_chi` this chi doesn't meet, and write nothing; `--force` installs it anyway. [docs/plugins.md](docs/plugins.md)
|
|
22
|
+
- `chi bundle upgrade NAME` upgrades a bundle from a folder, archive or git URL from the source its record keeps: the name alone is enough.
|
|
23
|
+
- "Stable for bundles" now covers the manifest schema. [docs/plugins.md](docs/plugins.md)
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
- `chi bundle diff NAME FILE` shows the bundle's new text for a file that stays behind, so the sections the "stays behind" line names can be read.
|
|
28
|
+
- `chi bundle try` no longer passes the user's dropped settings (guardrails, memories, hooks, log…) to its sandboxed chi through the environment.
|
|
29
|
+
- A bundle's `context_providers:` are skipped when this chi is older than the bundle's `requires_chi`.
|
|
30
|
+
- Reinstalling a bundle from the same source says what it did: `Up to date:` for unchanged files, and a last line naming what was rewritten and which of your memories were kept.
|
|
31
|
+
- A `files:` entry with no checksum no longer warns "Checksum mismatch" on every install.
|
|
32
|
+
- Objectives proposal cards no longer appear after a canceled or failed turn.
|
|
33
|
+
- A `delegate` child in a linked worktree is no longer told to re-delegate to the main checkout.
|
|
34
|
+
|
|
35
|
+
Update with `chi update` (agents-md 0.1.2, btw 0.1.4, loop-guard 0.3.10, mcp 0.7.6, skills 0.1.9, slow-commands 0.2.5: their `requires_chi` now names the chi that reads their settings).
|
|
36
|
+
|
|
10
37
|
## [0.151.0] - 2026-11-05
|
|
11
38
|
|
|
12
39
|
### Added
|
|
@@ -5690,7 +5717,8 @@ and long-lived sessions.
|
|
|
5690
5717
|
- A macOS desktop helper (`chi desktop install`): a "Send to chi" Service and a
|
|
5691
5718
|
hotkey panel that send selected text or the clipboard to your sessions.
|
|
5692
5719
|
|
|
5693
|
-
[Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.
|
|
5720
|
+
[Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.152.0...HEAD
|
|
5721
|
+
[0.152.0]: https://github.com/dm1try/samagotchi/compare/v0.151.0...v0.152.0
|
|
5694
5722
|
[0.151.0]: https://github.com/dm1try/samagotchi/compare/v0.150.0...v0.151.0
|
|
5695
5723
|
[0.150.0]: https://github.com/dm1try/samagotchi/compare/v0.149.0...v0.150.0
|
|
5696
5724
|
[0.149.0]: https://github.com/dm1try/samagotchi/compare/v0.148.0...v0.149.0
|
data/docs/bundle-authoring.md
CHANGED
|
@@ -129,10 +129,10 @@ class, a hand-built `ctx`), that is a gap in the kit: say so to the user rather
|
|
|
129
129
|
- **Shas.** Every file the manifest names has a sha256 line, and an edit makes it stale: the install then warns
|
|
130
130
|
of a checksum mismatch, and a line with no sha installs unchecked. `chi bundle check --fix` rewrites them; run it
|
|
131
131
|
after the last edit.
|
|
132
|
-
- **requires_chi.** Use the chi the user runs (`chi --version`)
|
|
133
|
-
newer than the floor (`ctx.http`
|
|
134
|
-
|
|
135
|
-
0.
|
|
132
|
+
- **requires_chi.** Use the chi the user runs (`chi --version`): a bundle whose `requires_chi` that chi doesn't
|
|
133
|
+
meet is now **refused at install** (`--force` is the only way past it), not just unloaded. `check` warns when the plugin calls something newer than the floor (`ctx.http` 0.80.0, `ctx.cost` 0.83.0, `ctx.usage` 0.100.0, `ctx.sessions.children` 0.34.0),
|
|
134
|
+
or a keyword added later (`ctx.ask_model(model:)` 0.80.0, `ctx.sessions.send(delivery:)` 0.64.0, `chi.toolkit(prompt:)` 0.72.0, `chi.toolkit(groups:)` 0.121.0), written on `ctx` / `chi` itself rather than through a variable,
|
|
135
|
+
or when the manifest itself uses a key newer than the floor (`block_renderers:` 0.151.0, `settings:` 0.143.0, `session_settings:` 0.84.0, `context_providers:` 0.32.0): an older chi ignores that key.
|
|
136
136
|
- **A typo in an API call.** `check` warns (`api`) on a call or keyword this chi lacks, on any path; for a newer chi on purpose, guard it with `ctx.respond_to?(:name)`.
|
|
137
137
|
- **trust_level.** Leave it out. `reviewed` / `trusted` are for someone who reviewed the bundle, not its author.
|
|
138
138
|
- **Restart.** A session loads bundles when it starts: after an install or a settings change, the user starts a
|
data/docs/cli.md
CHANGED
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
- `chi self` — print version, source dir (checkout or installed gem), config/memory/session paths, model/host and bundles. Run by a session's `execute`, the `model` row (and host, loop, profile, thinking, served model) is **that session's** model, labelled `(this session <id8>; default …)`; elsewhere it is the default, `(default)`. `model key` is the memory overlay key (`<name>.<key>.md`), and `big memories` the memories over `memory.entry_warn_tokens` ([Memory](memory.md#a-memorys-size)), and `model notes` the model notes a prompt of that model loads (`model_notes_deepseek (system, 612 chars)`, `none`; inside a session its `--mute` list is left out). `chi self --model` prints only the model a new session starts on, an alias resolved to its target (the desktop helper's hint)
|
|
37
37
|
- `chi update [--dry-run] [--to VERSION] [--json] [--no-gem] [--no-bundles] [--no-desktop]` — update an installed chi: the gem, the system bundle, the shipped bundles you installed and the desktop helper, and check the bundles from elsewhere, in one table (see [Updating](#updating))
|
|
38
38
|
- `chi home`, `chi home set [--ruby PATH]`, `chi home doctor` — the chi home: the one Ruby (and its gem path) every chi launch boots, whoever launches it. `chi home` shows it (its Ruby, the newest chi in it, gem home and path, `mise`, when and by which chi it was set, and the chi you ran when that is another one; exit 1 with the problem when it can't boot chi), `set` records this chi's Ruby and gem path (or `--ruby PATH`'s own; it must have samagotchi), `doctor` checks it and what should follow it, one row each with `ok`, `warn` or `fail` and what to run (exit 1 on a `fail`; see [Checking it](configuration.md#checking-it-chi-home-doctor)). At a terminal, a chi on another Ruby than the home's says so in one dim line a day. This machine only (see [The chi home](configuration.md#the-chi-home))
|
|
39
|
-
- `chi bundle install|upgrade|uninstall|status|diff|list|build|trash|new|test|check` — manage memory bundles (see [Bundle hooks](hooks.md#bundle-hooks-unified-workflow-bundle)); `list` shows the installed ones and the ones shipped with chi, which `install <name>` installs; `core` and `dev` are profiles that install a set of them (see [Bundle profiles](memory.md#bundle-profiles-core-and-dev), [Guardrails](guardrails.md), [Bundles](bundles.md#the-btw-bundle), [the mcp bundle](bundles.md#the-mcp-bundle) [the loop-guard bundle](bundles.md#the-loop-guard-bundle), [the check-in bundle](bundles.md#the-check-in-bundle) and [the skills bundle](bundles.md#the-skills-bundle)). `trash` lists and empties the bundle trash (moved files from uninstalls/upgrades). A usage error (an unknown subcommand or flag, a missing argument, a bad `build --scope`) exits 2, as for the other subcommands. Plain `chi` and `chi web` exit 1 on an unknown flag, a flag missing its value or a stray argument. `new`, `test` and `check` are for writing one: see [Writing a bundle](#writing-a-bundle-new-test-check)
|
|
39
|
+
- `chi bundle install|upgrade|uninstall|status|diff|list|build|trash|new|test|check` — manage memory bundles (see [Bundle hooks](hooks.md#bundle-hooks-unified-workflow-bundle)); `list` shows the installed ones and the ones shipped with chi, which `install <name>` installs; `upgrade <name>` upgrades an installed bundle by name from the source it came from (its folder, archive or git URL), `upgrade <source>` from a new one; `core` and `dev` are profiles that install a set of them (see [Bundle profiles](memory.md#bundle-profiles-core-and-dev), [Guardrails](guardrails.md), [Bundles](bundles.md#the-btw-bundle), [the mcp bundle](bundles.md#the-mcp-bundle) [the loop-guard bundle](bundles.md#the-loop-guard-bundle), [the check-in bundle](bundles.md#the-check-in-bundle) and [the skills bundle](bundles.md#the-skills-bundle)). `trash` lists and empties the bundle trash (moved files from uninstalls/upgrades). `install`/`upgrade` `--force` overwrites existing entries and also installs a bundle whose `requires_chi` this chi doesn't meet; without it such a bundle is refused (exit 1), nothing written. A plain `install` of a bundle that is already installed from the same source rewrites its hooks, rules, plugin, scripts and record: the summary lists a file that already holds the bundle's bytes as `Up to date: …`, one you wrote as `Skipped: …`, and ends with `NAME was already installed from SOURCE: its hooks, rules, plugin and scripts now match it and its record was rewritten` (plus, when a memory was kept, `your own copies of its memories were kept (chi bundle upgrade NAME merges the bundle's changes into them)`). A usage error (an unknown subcommand or flag, a missing argument, a bad `build --scope`) exits 2, as for the other subcommands. Plain `chi` and `chi web` exit 1 on an unknown flag, a flag missing its value or a stray argument. `new`, `test` and `check` are for writing one: see [Writing a bundle](#writing-a-bundle-new-test-check)
|
|
40
40
|
|
|
41
41
|
- `chi guardrails replay [--dry-run] [--threshold T] [--model M]` — seed System 1's record from the approvals this machine's sessions already answered: each ask is judged by the configured model and written to `guardrails/judgments.jsonl` with the answer the user gave, then a per-rule table prints (asks, allowed, denied, would-be auto-allows, false allows, asks with no judgment). A parent agent's answer is not the user's: only its deny is replayed, and only where the parent could have allowed. `--dry-run` judges without writing, `--threshold` overrides `guardrails.system_one.threshold` for the table, `--model` the configured model for this run. Exit 2 when `guardrails.system_one.mode` is off or no `host` is set; a usage error exits 2 (see [System 1](guardrails.md#system-1-experimental))
|
|
42
42
|
- `chi guardrails rule <id>` — print exactly `asks` when that rule is loaded, not disabled and votes `ask` in this `guardrails.mode` for every model; any other line says why not (no such rule, disabled, another mode, it denies, for some models only, rules that failed to load, `guardrails.enabled: false`). Exit 0 either way, 2 for a usage error. It reads config.yml and the installed bundles as a worker does (`GuardrailWiring#rules`) and takes the rule's id, never a command; the [coordinator skill](bundles.md#the-coordinator-bundle) reads it before it merges without asking
|
|
@@ -133,8 +133,10 @@ The loop for a bundle you're writing (the whole path: [bundle-authoring.md](bund
|
|
|
133
133
|
chi's Ruby (minitest is one of chi's gems; rspec is optional). The tests run the bundle's code as written, with
|
|
134
134
|
your environment (the kit sandboxes each test's HOME and XDG dirs, not the process).
|
|
135
135
|
- `chi bundle install --dev PATH` installs the bundle in the local directory `PATH` (not a git URL, archive or name:
|
|
136
|
-
exit 2) as a link to it: its plugin, hooks, rules and
|
|
137
|
-
next worker or `chi web` restart with no re-install
|
|
136
|
+
exit 2) as a link to it: its plugin, hooks, rules, scripts and `manifest.yml` are `PATH`'s own files, so an edit applies at the
|
|
137
|
+
next worker or `chi web` restart with no re-install (a manifest edit to `block_renderers:` shows in chi web's next
|
|
138
|
+
request; a new `scripts:`, `hooks:` or `plugin:` entry needs `chi bundle install --dev PATH` again, since the record
|
|
139
|
+
has no entry for it), and their sha256 isn't checked, for this bundle only (the
|
|
138
140
|
record says `dev`; every other bundle is checked as before). Its memories are copied at install. `chi bundle
|
|
139
141
|
status` shows `NAME vX dev → /abs/path`. `chi bundle upgrade NAME` refuses it (it follows its source already; to
|
|
140
142
|
leave dev mode: `chi bundle uninstall NAME`, then `chi bundle install` without `--dev`), as does a plain install
|
|
@@ -147,9 +149,13 @@ The loop for a bundle you're writing (the whole path: [bundle-authoring.md](bund
|
|
|
147
149
|
so run it (and `test`) on code you'd install. The codes, in order: `manifest` (a parse error, verbatim; the run
|
|
148
150
|
stops), `missing-file`, `unlisted` (warn: a file an install leaves out, by `MemoryFiles`' rules, so a README
|
|
149
151
|
isn't one), `sha` (a stale or placeholder sha, or a plugin / hook / script entry with no sha line; `--fix`
|
|
150
|
-
rewrites the first two and prints `fixed sha FILE → sha256:…`), `
|
|
152
|
+
rewrites the first two and prints `fixed sha FILE → sha256:…`), `unknown-key` (warn: a manifest key this chi's
|
|
153
|
+
parser never reads, so it ignores it, one row per key at its line; the install keeps the manifest, so a newer
|
|
154
|
+
chi reads the key from it with no reinstall, and `chi update`'s re-check names it too), `requires-chi` (this chi doesn't satisfy it:
|
|
151
155
|
error; missing on a bundle with code: warn), `requires-chi-floor` (warn: the plugin calls an API newer than the
|
|
152
|
-
oldest chi `requires_chi` admits, from `Plugin::Api::SINCE
|
|
156
|
+
oldest chi `requires_chi` admits, from `Plugin::Api::SINCE`, or the manifest itself uses a key newer than it, from
|
|
157
|
+
`Manifest::SINCE`; an installed bundle gets the unknown-key row but never the floor, as it's the author's fix),
|
|
158
|
+
`trust` (warn: `trust_level: reviewed|trusted` on a
|
|
153
159
|
bundle chi doesn't ship), `clash` (warn: a name chi ships, or one installed from elsewhere; the sandboxed install
|
|
154
160
|
then forces), `install` (each Installer warning, a `needs` one as warn, and each plugin or hook load failure at
|
|
155
161
|
the source `file:line`; skipped while a placeholder sha is unfilled), `registered` (info: each command, tool,
|
|
@@ -313,7 +319,7 @@ Also shipped, not installed: dev (mcp, skills, source-links) (chi bundle install
|
|
|
313
319
|
file in the bundle trash and `merged 0.8.0 into your FILE` in the row. A real
|
|
314
320
|
conflict, no `git`, or no recorded base keeps your file untouched, and the row
|
|
315
321
|
says `FILE stays behind 0.8.0:` with why and what it missed (up to three headings
|
|
316
|
-
and a count, or `2 places`). `chi bundle diff NAME FILE` shows it; `chi bundle
|
|
322
|
+
and a count, or `2 places`). `chi bundle diff NAME FILE` shows it (the base, your file, then the bundle's new text); `chi bundle
|
|
317
323
|
upgrade NAME --force` takes the bundle's, with a copy of yours in the bundle
|
|
318
324
|
trash first. A later run retries such a file at the same version: its row reads
|
|
319
325
|
`0.8.0 merged 0.8.0 into your FILE` when it merges, and names it as behind when
|
|
@@ -367,6 +373,26 @@ Also shipped, not installed: dev (mcp, skills, source-links) (chi bundle install
|
|
|
367
373
|
update` again). A failed gem install checks against this chi, since it stays.
|
|
368
374
|
- `--no-bundles` (`update.bundles: false`) leaves these out too: no check, no row
|
|
369
375
|
but `bundles skipped`.
|
|
376
|
+
- **Bundles installed by an older chi** have no kept `manifest.yml` (it didn't
|
|
377
|
+
keep it), so chi reads what their install recorded, and a manifest
|
|
378
|
+
key that chi didn't know (`block_renderers:` before 0.151.0) is missing. Where
|
|
379
|
+
chi has the bundle's own manifest at the installed version, the update keeps it,
|
|
380
|
+
once: a shipped bundle from chi's shipped copy (`up to date (kept its manifest)`;
|
|
381
|
+
the system bundle at the next chi start), a bundle from a folder or a zip/tar
|
|
382
|
+
that is still there from it (a `kept its manifest` row; the archive is extracted
|
|
383
|
+
and the extract removed). Only when the source is what's installed: the same
|
|
384
|
+
name and version, the same manifest values as the install recorded (a key the
|
|
385
|
+
older chi didn't know aside), and the same memories, plugin, hooks, scripts and rules. A git source
|
|
386
|
+
isn't cloned: the row names the command,
|
|
387
|
+
`installed by an older chi, which didn't keep its manifest: chi bundle upgrade NAME reads it again from URL`.
|
|
388
|
+
A source that's gone or doesn't match gets its own row (why, and what to run: for
|
|
389
|
+
a gone one, `NAME's recorded source S is gone: chi bundle upgrade <path or URL of a copy>`). The
|
|
390
|
+
same goes for a record an older chi rewrote after a newer one kept the manifest
|
|
391
|
+
(`an older chi rewrote its record after the install, so its manifest.yml may be an
|
|
392
|
+
earlier version's`). `--dry-run` says `would keep its manifest` and writes nothing.
|
|
393
|
+
An edited `manifest.yml` isn't kept over: its row says so (`chi bundle upgrade
|
|
394
|
+
NAME`). These rows are warnings and fail nothing; `chi bundle status NAME` says the
|
|
395
|
+
same on its `Manifest:` line.
|
|
370
396
|
- **Profiles** (`core`, `dev`): an installed one installs the bundles a new
|
|
371
397
|
chi added to it (`updated (+ name)`), even at the same version, and retries
|
|
372
398
|
one that failed before; one you uninstalled stays out. See [Bundle
|
|
@@ -907,7 +933,6 @@ the model's `objectives` tool); [Objectives](sessions.md#objectives) has what th
|
|
|
907
933
|
- `/objectives done O1 [O2…]`, `/objectives drop O1 [O2…]`: mark items. An id it doesn't have refuses the whole line, and
|
|
908
934
|
so does `done` on a dropped item. The model is shown a done item as `O1 [done] …` and no dropped one.
|
|
909
935
|
- `/objectives undo O1 [O2…]`: back to active, after a mistaken `done` or `drop`.
|
|
910
|
-
|
|
911
936
|
- `/objectives check O1`: an independent read of O1's latest move, one side request; the verdict comes as a card and as
|
|
912
937
|
`check: …` under the move. Information only: it edits nothing, and it is refused for an objective with no move or a dropped one, and during a participant's turn. See [Objectives](sessions.md#objectives).
|
|
913
938
|
|
data/docs/guardrails.md
CHANGED
|
@@ -1157,6 +1157,10 @@ the whole call:
|
|
|
1157
1157
|
|
|
1158
1158
|
`decision` is what the judgment led to: `audit` (audit mode), `asked` (on
|
|
1159
1159
|
mode, the user was asked), `auto` (on mode, System 1 answered for the user).
|
|
1160
|
+
When two rules ask about one call the line's `rule` is the one the card names
|
|
1161
|
+
and `other_rules` lists the rest (`Verdict#other_ask_rules`), so a second
|
|
1162
|
+
rule's record (`SystemOne::Record`) counts the ask too; a line with one rule
|
|
1163
|
+
has no `other_rules` at all.
|
|
1160
1164
|
A judgment that never arrived is logged too, with `p` null, so the failures
|
|
1161
1165
|
count as well. The label line, keyed by the judgment's id, is what the user
|
|
1162
1166
|
did: `allow_once`, `allow_wide` (any scope wider than once), `deny`, or
|
|
@@ -10,7 +10,9 @@ model. The TUI is untouched: it shows a fence as source.
|
|
|
10
10
|
in document order and zips them with the fragment's `<pre>`s, setting `data-lang` after sanitize's walk. Only the first
|
|
11
11
|
word, downcased, matching `BlockRenderers::LANG`. A count that differs sets nothing. **`web.markdown` off means no
|
|
12
12
|
`<pre data-lang>`, so nothing draws.**
|
|
13
|
-
2. `BlockRenderers.for_installed` (manifest `block_renderers:`, read
|
|
13
|
+
2. `BlockRenderers.for_installed` (manifest `block_renderers:`, read through `InstalledBundle#manifest`: the bundle's
|
|
14
|
+
kept `manifest.yml`, else its record's mirror, so chi web needs no plugins, and a bundle installed by a chi that
|
|
15
|
+
didn't know the key draws once a kept manifest is there) lists
|
|
14
16
|
who draws what: `GET /api/block_renderers`, `:owner`. `GET /api/block_renderers/:bundle/:file` serves a file, `:owner`:
|
|
15
17
|
the URL's names go through `BlockRenderers.script_path` (membership in a listed renderer's `load` before any path is
|
|
16
18
|
made), then `Provenance.sha_matches?`; a changed file is 409 `renderer_changed`. ETag + `no-cache`, as static files.
|
data/docs/internals/bundles.md
CHANGED
|
@@ -13,11 +13,14 @@ user-facing side (what a bundle owns, profiles, needs) is in [memory.md](../memo
|
|
|
13
13
|
*.md, index.md the bundle's memories, one managed index line each ("· from <bundle>")
|
|
14
14
|
<system memories>/.bundles/ MemoryPaths.bundles_dir
|
|
15
15
|
<name>/manifest.json the install record (Provenance#write, read as InstalledBundle)
|
|
16
|
+
<name>/manifest.yml the bundle's own manifest as installed (Provenance#write's manifest_from:), its sha
|
|
17
|
+
the record's manifest_sha; a dev install links it to the source (DEV_SHA)
|
|
16
18
|
<name>/bases/ each file's base for the next upgrade's 3-way merge: as installed, or the
|
|
17
19
|
bundle's own text after a merge
|
|
18
20
|
<name>/incoming/ the bundle's text for each file marked conflict, until the file settles (Provenance:
|
|
19
|
-
#write, #refresh_marks, gone with #resolve_conflicts
|
|
20
|
-
|
|
21
|
+
#write, #refresh_marks, gone with #resolve_conflicts; #prune_incoming takes a settled file
|
|
22
|
+
at any depth and the dirs it leaves empty; read by BundleCommand#print_incoming for `chi bundle diff`);
|
|
23
|
+
nothing globs it for memories (Builder takes the scope dir's top level), and an uninstall's rm_rf sweeps it
|
|
21
24
|
<name>/hooks/ guardrails/ plugin/ scripts/
|
|
22
25
|
.trash/<name>-<stamp>/ files an uninstall or upgrade took away, and copies of what an install
|
|
23
26
|
overwrote (Trash), never deleted
|
|
@@ -27,32 +30,35 @@ user-facing side (what a bundle owns, profiles, needs) is in [memory.md](../memo
|
|
|
27
30
|
|
|
28
31
|
| Piece | Role |
|
|
29
32
|
|---|---|
|
|
30
|
-
| `BundleCommand` (`lib/samagotchi/bundle_command.rb`) | `chi bundle install/upgrade/uninstall/status/diff/list/build/trash/new/test/check/try` (`install --dev`: the Installer's `dev:`, links and `Provenance::DEV_SHA`); runs the Installer, prints its summary, runs the conflict agent step; refuses a source already installed under another name (`installed_under_another_name?`: a git URL bundle recorded under its repo's name before the manifest named it) |
|
|
33
|
+
| `BundleCommand` (`lib/samagotchi/bundle_command.rb`) | `chi bundle install/upgrade/uninstall/status/diff/list/build/trash/new/test/check/try` (`install --dev`: the Installer's `dev:`, links and `Provenance::DEV_SHA`); runs the Installer, prints its summary, runs the conflict agent step; refuses a source already installed under another name (`installed_under_another_name?`: a git URL bundle recorded under its repo's name before the manifest named it). `upgrade` resolves its argument by `upgrade_source`: a git URL or an existing path, then a shipped name, then the bare name of an installed bundle — whose record's source it then uses (a git URL with its `#ref`, a dir or an archive that is still there), printed as `Upgrading NAME from SOURCE`; a dev record is refused with `Installer.dev_install_line`, a recorded source that is relative, empty or gone says so and asks for the path or URL of a copy (`Installer.source_gone_line`, the words `ManifestStanding` uses too) |
|
|
31
34
|
| `BundleCommand::Check` (bundle_command/check.rb) | `chi bundle check [PATH] [--fix] [--set K=V]`: a bundle being written, checked as an install takes it (docs/cli.md lists the codes); parses the manifest from `Shas.parseable` (a placeholder sha is a `sha` finding, not a YAML error), reads the user's installed bundles only for `clash`, and installs and loads through `Testing::Install` in a `Testing::Sandbox` with `force: true`, the log's echoes swallowed |
|
|
32
|
-
| `BundleCommand::Try` (`bundle_command/try/`) | `chi bundle try PATH -p TEXT`: `Command` (its flags, help and checks) drives the rest and `Render` prints the trace; a filtered-config `Sandbox` (its folders a `SandboxLayout`, the layout `Testing::Sandbox` uses too) installs the tried bundle and shipped `guardrails` / `cost-cap` in a separate child, then `Run` starts one non-interactive chi process from its realpath work dir; `TraceReader` reads that session's analytics, cards (`Bridge::CardStore.saved`, as the web does) and session JSON by id (never `Session.list`), joining timing and tool args for text or experimental JSON output (a turn a `--timeout` interrupt cut keeps its calls like a Ctrl-C, the one cut short answered as interrupted, so its rows come from the saved calls too; `Run::Result#timed_out` becomes `timed_out`). `Sandbox#child_env` is a Hash, never an ENV mutation; it keeps ordinary user model settings and Keychain access while dropping the spawner's session/guardrail/worker markers (`ChildEnv`, the scrub a session's worker spawn uses too). The child runs in its own process group: a `--timeout` sends it INT, then `ProcessGroup.stop` (the groups its processes started too); after any exit the group is signalled only while a straggler is still in it, never a reaped group. `Sandbox#stop_leftovers` (before the trace, and in `cleanup`) stops what left the child's tree, found by the sandbox's own records: a delegate's worker by its session's `owner.lock`, a task by its running `task.json` whose pid still leads its group. |
|
|
35
|
+
| `BundleCommand::Try` (`bundle_command/try/`) | `chi bundle try PATH -p TEXT`: `Command` (its flags, help and checks) drives the rest and `Render` prints the trace; a filtered-config `Sandbox` (its folders a `SandboxLayout`, the layout `Testing::Sandbox` uses too) installs the tried bundle and shipped `guardrails` / `cost-cap` in a separate child, then `Run` starts one non-interactive chi process from its realpath work dir; `TraceReader` reads that session's analytics, cards (`Bridge::CardStore.saved`, as the web does) and session JSON by id (never `Session.list`), joining timing and tool args for text or experimental JSON output (a turn a `--timeout` interrupt cut keeps its calls like a Ctrl-C, the one cut short answered as interrupted, so its rows come from the saved calls too; `Run::Result#timed_out` becomes `timed_out`). `Sandbox#child_env` is a Hash, never an ENV mutation; it keeps ordinary user model settings and Keychain access while dropping the spawner's session/guardrail/worker markers (`ChildEnv`, the scrub a session's worker spawn uses too). Every dropped key is sent as nil, so the child really lacks it: a spawn env is merged onto the spawner's, so a key only deleted from the Hash is still inherited (`ChildEnv.spawn_env`). The child runs in its own process group: a `--timeout` sends it INT, then `ProcessGroup.stop` (the groups its processes started too); after any exit the group is signalled only while a straggler is still in it, never a reaped group. `Sandbox#stop_leftovers` (before the trace, and in `cleanup`) stops what left the child's tree, found by the sandbox's own records: a delegate's worker by its session's `owner.lock`, a task by its running `task.json` whose pid still leads its group. |
|
|
33
36
|
| `BundleCommand::ApiCalls` (bundle_command/api_calls.rb) | what a plugin's source calls of the API, by Prism: `#findings` (a call or keyword the API's classes lack, `check`'s `api`), `#keywords` (the floor check's `call(keyword:)`). Its only list is `MEMBERS`, which object a member gives (`ctx.sessions` → `Plugin::Sessions`); a new such object joins it |
|
|
34
37
|
| `BundleCommand::Scaffold` (bundle_command/scaffold.rb) | `chi bundle new NAME`: the starting files (block-form `plugin:` entry, `requires_chi` at this chi, a kit test that passes as written); the manifest's shas are filled through `Shas.refresh` |
|
|
35
38
|
| `BundleCommand::TestRun` (bundle_command/test_run.rb) | `chi bundle test`: `RbConfig.ruby -I <chi's lib>` on `test/**/*_test.rb` (Minitest) or `spec/` (RSpec, as `rake bundles:specs`), under `Bundler.with_unbundled_env` when Bundler's runtime is loaded |
|
|
36
39
|
| `Shas` (shas.rb) | a manifest's `sha256:` lines: `.refresh(dir[, text])` rewrites them line by line (the layout kept; a `files:` entry it can't place raises `Shas::Error`), `.stale` → `[Stale(file, old, new)]` (`old` `""` for a placeholder, nil for a plugin / hook / script entry with no sha line), `.parseable` (a placeholder `sha256: sha256:` isn't YAML). Plain Ruby: `rake bundles:sha` / `bundles:check` (`script/release/release_tools.rb`) and `chi bundle check --fix` share it |
|
|
40
|
+
| `FileDigest` (file_digest.rb) | the one digest of a file on disk: `.hex(path)` (its bytes, streamed; nil when the path isn't a file, a raise for one that can't be read), `.sha256(path)` = `"sha256:<hex>"`, nil with no file — the shape a record writes for a hook, rule, plugin or script. `Provenance.file_sha`, `Merger.digest`, `Status.checksum`, `InstalledBundle#source_sha`, `FileStanding.differs?` and the Installer's checksum verifiers all go through it; a digest of bytes already in memory (a merge's text, a memory's content that is also its base) stays with the code holding them |
|
|
37
41
|
| `SourceNormalizer` (source.rb) | source → local dir; a git/zip/tar extract is *owned* and removed after the install |
|
|
38
42
|
| `SourceIdentity` (source.rb) | what a source is, for comparing two installs of one name: `shipped:<dir>` (any gem or checkout), `git:<url>` (no `#ref`, trailing `/` or `.git`), `path:<realpath>`; nil when it can't place one |
|
|
39
43
|
| `PreparedSource` (source.rb) | one source normalized once (dir, owned, git commit): `BundleCommand` reads the bundle's name from its `manifest.yml` (the name for every kind of source, a git URL too) and hands it to the Installer (`prepared:`), so a source is cloned or extracted once per command |
|
|
40
44
|
| `MemoryFiles` | which top-level `*.md` files of a bundle are its memories (`.select`: the `files:` list, else all but README/LICENSE and the like), `.unshipped` (listed, not shipped) and `.symlinks` (skipped: a symlink is never a memory) |
|
|
41
|
-
| `Manifest` | parses/validates `manifest.yml` (`name:` is the bundle's name and a directory under `.bundles/`, so a path such as `../x`, `a/b`, `.` or `..` is refused: `name: … is not a bundle name`; YAML that doesn't parse or isn't a mapping is a `ValidationError` too; files, hooks as `BundleHook`, plugin, scripts, needs, requires_chi, includes, context_providers, session_settings: the keys a session's `chi --set` may give, a list or a mapping of them to types (`SESSION_SETTING_TYPES`), recorded at install in `manifest.json` (`session_settings`, `session_setting_types`) for `SessionConfig` to check; settings: the keys config.yml's `bundles: <name>:` may have, the same shape, recorded as `settings` (nil when the manifest has none) and `setting_types` for `BundleSettingsCheck`, whose warnings the Engine holds for the first turn and `SessionConfig`'s refusals share the words of) and writes it (`Manifest.write`, for Builder). It ignores `requires_chi_next`, a shipped bundle's release-time marker (`script/release/release_tools.rb`, docs/releasing.md) |
|
|
45
|
+
| `Manifest` | parses/validates `manifest.yml` (`name:` is the bundle's name and a directory under `.bundles/`, so a path such as `../x`, `a/b`, `.` or `..` is refused: `name: … is not a bundle name`; YAML that doesn't parse or isn't a mapping is a `ValidationError` too; files, hooks as `BundleHook`, plugin, scripts, needs, requires_chi, includes, context_providers, session_settings: the keys a session's `chi --set` may give, a list or a mapping of them to types (`SESSION_SETTING_TYPES`), recorded at install in `manifest.json` (`session_settings`, `session_setting_types`) for `SessionConfig` to check; settings: the keys config.yml's `bundles: <name>:` may have, the same shape, recorded as `settings` (nil when the manifest has none) and `setting_types` for `BundleSettingsCheck`, whose warnings the Engine holds for the first turn and `SessionConfig`'s refusals share the words of) and writes it (`Manifest.write`, for Builder). It ignores `requires_chi_next`, a shipped bundle's release-time marker (`script/release/release_tools.rb`, docs/releasing.md). `KEYS` is every top-level key it reads, so a key outside it is one this chi ignores: `#unknown_keys`, in the order the manifest lists them, said in `Manifest.unknown_key_line`'s words by the Installer and `chi bundle check`. `SINCE` is the first release that read each key or shape (taken from the tags), and `#floor_keys` names the ones this manifest uses — including the shapes only a newer chi reads (a `files:` mapping, a hook's `events:`, a typed `session_settings:`/`settings:`) — for check's `requires-chi-floor` |
|
|
42
46
|
| `Installer` | one install/upgrade/dry run of one bundle (below) |
|
|
43
47
|
| `AssetInstaller` | the parts that live in `.bundles/<name>/`: hooks, guardrail rules, plugin, scripts; returns `AssetInstaller::Installed` (the paths the record keeps). A file it replaces or removes whose bytes are neither the recorded sha nor the incoming file's is copied to the Installer's `Trash` first (`Trash#copy_edit`, `#copy_edits` for a folder at any depth, as `hooks/x`, `guardrails/x`, `plugin/x`, `scripts/sub/x`; no sha recorded counts as edited; a dev link never is) |
|
|
44
48
|
| `Merger` | 3-way classification per file: `.classify` → an `Outcome` (`status`: `install`, `fast_forward`, `keep`, `noop`, `conflict`, `merged`; `base`: where the file's next base comes from, `:on_disk`, `:recorded` or `:incoming`; a new outcome and its base go in its constants, and it needs a branch in `Installer#merge_memory`, a kind in `#next_base` if its base is new, and its status word in `#preview_memory`, `#verify_memory_checksums` and `#summary`). `.classify` never merges and answers `conflict` for two changed sides; `Merger::Upgrade` (provenance, target dir, source dir) is the one way to ask what an upgrade does to a file, nothing written: `#outcome` (`.classify`, then `TextMerge` where both sides changed, git once per call: `merged`, with its `TextMerge::Result` and the digest it read as the Outcome's `merge` and `current_sha`; `ALREADY_MERGED`, a `keep` with base `:incoming`, when the merge adds nothing; or `conflict` with that `merge`, whose `why` says why none was possible), `#classified`, `#forecast` (a `Forecast` of `merged` and `behind` in one pass), `#behind` (a `conflict` outcome as a `FileStanding::Behind`: why, and from `TextMerge.changed_sections` what it missed, nothing for an older mark, whose base may be the user's text), `#paths`, used by the Installer's merge and dry run, `ShippedUpdate.plan` and `SystemBundle.sync(dry_run:)`. `.write_merged` puts a merge in place: the digest checked just before the rename (`AtomicFile.write`'s block), through a symlink, the mode kept, the old file in the `Trash`; false when a live `memory_write` got in. A mark from an older chi (`InstalledBundle.legacy_mark?`: no `incoming_sha`, or `legacy_base`, which keeps that known once the digest is recorded, until the mark goes) may have the user's own text as its base, 0.149's doing, and a merge against it would undo that text with nothing to show, or a fast-forward replace all of it: `#outcome` makes a fast-forward that takes a line out of the file, and `.merge_outcome` a merge that does, a `conflict` (`FileStanding::LEGACY_BASE`), on the Installer's and `Remerge`'s path alike, unless `ShippedHistory` proves the base is a version chi shipped. The price, with no such gem left or for a bundle chi doesn't ship: a real base looks the same, so such a file whose bundle rewords or removes a line it still holds stays behind until it is resolved by hand, with `--agent` or with `--force`; no retry fixes it. `Installer#save_marked_file` serves a fast-forward that goes ahead over a mark: a file put back to its base under this chi's mark, or an older mark with a proved base or an update that only adds. `.markers?` (a file holding a merge's `<<<<<<<`/`>>>>>>>` lines; `=======` alone is a Markdown underline) |
|
|
45
49
|
| `ShippedHistory` | `.shipped?(bundle, file, path)`: whether those bytes are that file of that bundle (by manifest name, since a shipped dir can be renamed) in an installed `samagotchi-<version>` gem. Reads `<gem dir>/gems/` by hand, for `Gem.path` (set by a chi home's boot string) and the home's `gem_dirs` from home.json (a checkout or another Ruby runs on other gems), never `Gem::Specification`. Size, then bytes, then the manifest. Anything it can't read is no match, which keeps the guard. Asked only when `Merger`'s guard would otherwise fire: with 54 gems on the Studio a miss costs about 4 ms, a hit less |
|
|
46
50
|
| `TextMerge` | the text merge: `.merge(base:, ours:, theirs:)` → a `Result` (`status` `:clean` with `text`, `:conflict` with `hunks`, `:failed` with `why`). `git merge-file -p --diff3 --marker-size=20` on temp copies in a temp cwd, with every `GIT_*` that points at a repository or a config unset or at `/dev/null` (it can run inside another tool's hook). git isn't a runtime dependency: none on PATH, or macOS's `/usr/bin/git` with no developer tools behind it (`.apple_stub?`, by `xcode-select -p`; running it pops a dialog), is `:failed`. So is a git past `TIMEOUT` (10 s; its process group is killed: this runs under `SystemBundle`'s lock), and any doubt about the answer: `min(hunks, 127)` against the exit status, a status over 127, a NUL in a side, marker lines that don't pair up (a user's line equal to one), a line no input holds, or an empty answer to non-empty inputs. `Scan` reads the output: a hunk unions (ours, then the entries of theirs that ours lacks) only when the last heading before it, at level 1 or 2 and outside code fences, HTML comments and any hunk, is titled exactly `Changelog` or `Change log`, with nothing but list entries (an item and its indented lines) since that heading and no fence or comment open, and base, ours and theirs each hold only list entries, with every base entry still on both sides: both only added. Any other hunk is a real conflict, and markers never leave the module. An entry is an item and the lines indented deeper under it, nested items too, and the union compares whole entries. `.changed_sections(base, theirs, ours)`: the headings of the sections the bundle changed and the file hasn't got, and whether text under no heading did, which is what names a miss: a conflict's hunks can't, since the whole file stays and the one hunk may be a Changelog line beside a whole new section. `Hunk#heading` counts the places under no heading |
|
|
47
51
|
| `FileStanding` | the one owner of what chi says of an edited bundle file, used by every surface: `Behind` (`#state`, `#line`: `x.md stays behind 0.8.0: no merge possible (why); missed ## A, ## B, ## C and 2 more`, `would stay` in a dry run), `MergeNote`, `.merged_line`, `.no_merge` (for `LEGACY_BASE` it says chi can't tell whether the file holds edits), `.clauses` (a report's clauses for one bundle), `.behind_count`, `.stay_behind`, `.counts_sentence` (the push), `.still_behind`, `.text` (UTF-8, whatever a name or heading was tagged), `FORCE_TAKES`, `FORCE_MOVES`, `IN_THE_TRASH`. Behind means marked and bytes other than the mark's `incoming_sha` (`.behind?`, read by `InstalledBundle#behind_files` and `Remerge` alike): a file brought level by hand isn't, a missing one is a missing file, and one that can't be read is. An older mark no retry has looked at reads `may be behind` (`Behind#unsure`, and the counts say so), and isn't behind at all when level with its recorded source on disk (`InstalledBundle#behind_file?`). A new surface reads these and adds no wording |
|
|
48
|
-
| `Remerge` | the retry at the same version, memory files only, for a file marked `conflict`: `#run(dry_run:)` → a `Result` (`merged`, `settled`, `stamped`, `behind`, `warnings`, `trash_copy
|
|
49
|
-
| `Provenance` | the `.bundles/<name>/` dir: `#write` the record + bases (its `files:` is the full list: a key left out is dropped, a nil value keeps the entry with no base; `marks:` are the files left behind, each a `FileStanding::Behind`, and `incoming_dir:` the bundle text each speaks of, recorded as `incoming_sha` and saved at `#incoming_path`; an older mark that stays keeps `legacy_base`), `#resolve_conflicts` (base to the incoming file, mark and its fields (`MARK_FIELDS`) and saved text gone; a file whose incoming copy is gone keeps both), `#refresh_marks` (what a retry found: why, missed, the digest and `legacy_base`, the saved text), `#note_merged` and `#clear_merge_notes(notes)` (`MERGE_NOTE_FIELDS`, kept through `#write`; the saved text is best effort, after the manifest, so it can't leave a plugin under the old record's sha; any other failure writing the record still can, until a retry), `#record` → `InstalledBundle`, `.each_installed`, `.claimants` (which bundles own a file) |
|
|
50
|
-
| `InstalledBundle
|
|
52
|
+
| `Remerge` | the retry at the same version, memory files only, for a file marked `conflict`: `#run(dry_run:)` → a `Result` (`merged`, `settled`, `stamped`, `behind`, `warnings`, `trash_copy`). It merges what merges and settles those entries (`Provenance#resolve_conflicts`, the index line refreshed), leaving `version`, `installed_at` and the assets alone: the Installer would `rm_rf` guardrails, plugin and scripts under live sessions. Every file it leaves is in `behind`, with why: one not shipped now (`MemoryFiles.select`) keeps its mark, base and text; one whose `incoming_sha` isn't the shipped text (a checkout at one VERSION) is left alone, no git runs, and it reads as its mark says; a mark with no digest (older chi) is tried as well, and a real conflict then records it (`Provenance#refresh_marks`, which writes only when the mark would read differently, so a conflict at each start writes nothing). A mark whose digest matches is tried on every run, since the file or git may have changed. `noop` and `keep` settle quietly; `fast_forward` waits for the next upgrade, reading as behind until then; `install` is a missing file, which isn't behind, and a warning says so. A file it can't read or replace stays as it is, saying why. Called from `ShippedUpdate.plan_row`/`apply` (`chi update`) and `SystemBundle.sync`'s equal-version branch, under its lock (each chi start and `chi update`); a bundle chi doesn't ship retries through `chi bundle upgrade`. No marks: nothing is read beyond the record. One marked file costs one git run per Engine start, about 12 ms on the Studio (0.1 ms with none) |
|
|
53
|
+
| `Provenance` | the `.bundles/<name>/` dir: `#write` the record + bases (its `files:` is the full list: a key left out is dropped, a nil value keeps the entry with no base; `marks:` are the files left behind, each a `FileStanding::Behind`, and `incoming_dir:` the bundle text each speaks of, recorded as `incoming_sha` and saved at `#incoming_path`; an older mark that stays keeps `legacy_base`), `#resolve_conflicts` (base to the incoming file, mark and its fields (`MARK_FIELDS`) and saved text gone; a file whose incoming copy is gone keeps both), `#refresh_marks` (what a retry found: why, missed, the digest and `legacy_base`, the saved text), `#note_merged` and `#clear_merge_notes(notes)` (`MERGE_NOTE_FIELDS`, kept through `#write`; the saved text is best effort, after the manifest, so it can't leave a plugin under the old record's sha; any other failure writing the record still can, until a retry), `#record` → `InstalledBundle`, `.each_installed`, `.claimants` (which bundles own a file). `#write(manifest_from: DIR)` keeps `DIR/manifest.yml` at `#kept_manifest_path` (written aside and renamed; for `dev:` a link to its realpath) and records `manifest_sha` (`FileDigest.sha256`, `DEV_SHA` for dev) and `installed_by` (this chi's VERSION); the Installer and `Profile.install` pass their source dir, and a write without it carries the existing record's two fields, the kept file left alone. The in-place edits (`#resolve_conflicts`, `#record_restored`, `#refresh_marks`, `#note_merged`, `#clear_merge_notes`) keep them. The record's mirror of the manifest keys (`requires_chi` … `setting_types`) is still written from the kwargs, exactly as before: an older chi sharing the memories dir (an old worker, chi web, gem or worktree) reads only the record, and without the mirror's `requires_chi` it would load a too-new bundle's plugin and hooks. A key a later manifest gains is never mirrored. Any chi without the kept manifest that rewrites a record drops `manifest_sha` and `installed_by` (it rebuilds the record from its own kwargs). `#adopt_manifest(dir, dry_run:)` heals such a record (`InstalledBundle#manifest_unkept?`: no `manifest_sha`, or its file gone): when `dir` is the source of what's installed (its manifest names the bundle at the recorded version; each mirrored key, normalised as `.mirror_fields` records it (the one shape `#write` uses too), equals the record's, a key the record lacks being fine only when `Manifest::SINCE` has it, since an older chi couldn't have recorded it; each recorded hook's events, on_error and priority are the manifest's; and every recorded plugin, hook, script, rule and unmarked memory sha is that of `dir`'s file; else the why names the key or file) it keeps `dir/manifest.yml` and records `manifest_sha`, nothing else (not `installed_at`, the mirror or `installed_by`); otherwise it returns why and writes nothing. Never for a dev record (its next `install --dev` links the file) |
|
|
54
|
+
| `InstalledManifest` (installed_manifest.rb) | the one answer to "this installed bundle's manifest" (`InstalledBundle#manifest`): requires_chi, trust_level, hook metadata, needs, the scripts' names, context providers, block renderers, session settings and settings with their types, file descriptions, a profile's includes. Read from the kept `manifest.yml` (`origin: :kept`) when the record's `manifest_sha` is set and the file still has it (`Provenance.sha_matches?`, so a dev link is never checked), else from the record's `mirror` (`origin: :record`), in these cases: no kept file and no `manifest_sha` (a record from before chi kept it; no problem); no kept file, or something other than a file in its place, while `manifest_sha` is set (`:missing`); a kept file and no `manifest_sha` (a chi without the kept manifest rewrote the record: `problems["manifest.yml"] = :stale`); a kept file with another sha, or one that isn't a YAML mapping (`:edited`); the sha matches (`:kept`). `#standing` is that symbol, `#key_problems` the rest. The kept file is read by `Manifest.tolerant` key by key in a fixed order (its comment): a key that doesn't parse under this chi takes the mirror's value if that parses, else empty, with its message in `problems` (a reader that logged it logs that); `block_renderers` is checked against the record's scripts when the kept `scripts:` fails, and one bad hook entry keeps the record's hooks whole. Hooks: each *recorded* hook with the kept manifest's events, on_error and priority and the record's sha (a found `hooks/*.rb` with no entry as recorded; a manifest hook the record lacks isn't installed, so isn't listed). `description` and `unknown_keys` come only from a kept file; a profile's `includes` is always the record's (its members, identity). The value is deep-frozen, since callers share it. One process-wide cache (a Hash under a Mutex, cleared at `CACHE_LIMIT`) keyed by the record value and the kept file's stat (mtime, size, inode, device; a dev link's target): chi web asks per request, a worker at start and per `ContextProviders.resolve`. `#requires_chi_failure(version = VERSION)` is the one requires_chi gate; `#experimental?`, `#needs?`, `#context_providers?`, `#block_renderers?` |
|
|
55
|
+
| `ManifestStanding` (manifest_standing.rb) | the one owner of the words for an installed bundle's `manifest.yml` as `InstalledManifest` read it: `.state` (`:kept`, `:legacy`, `:stale`, `:missing`, `:edited`), `.label` (`[kept]` / `[from the install record]`), `.line(bundle, refused:)` (the case and the command: `chi bundle upgrade NAME` (reads it again from a git URL, or a source that didn't match, with why), `chi update keeps it` while untried, the source is gone (`Installer.source_gone_line`), `chi bundle install --dev SOURCE` for a dev record; for a kept one, only the keys whose `Manifest::SINCE` floor is above the record's `installed_by`: newer than its installer), `.keep(bundle, source_dir:, dry_run:)` → `Outcome` (the record after, the line, `kept`): `Provenance#adopt_manifest` from `source_dir`, else from the recorded source (a dir; a zip/tar through `PreparedSource`, cleaned up; never a git clone). Read by `ShippedUpdate` (`Row#manifest`, the plan's dry `keep` and apply's), `UpdateCommand#external_rows` (a `kept its manifest` row, else a warn row, before the bundle's re-check, which then sees the kept file) and `chi bundle status NAME` (`BundleCommand#manifest_line`: `Manifest:`, the label, `.status_line`: for a record with a folder source (a bundle chi ships: this chi's shipped copy at the recorded version, which `chi update` reads), a dry `adopt_manifest` first, so the line says whether `chi update` keeps it or why not; an archive isn't extracted there, its line says `chi update tries to keep it`; a profile chi doesn't ship is never kept by `chi update`, so its line names `chi bundle upgrade NAME`) |
|
|
56
|
+
| `InstalledBundle`, `BundleHook`, `PluginRef` | value objects for the record; tolerant of older records (missing keys read as empty). `InstalledBundle`'s members are identity and state (name, version, scope, source, `installed_at`, `source_commit`, files and their marks, hooks, guardrails, scripts and the plugin with their shas, a profile's `includes`, `dev`, `dir`, the kept manifest's `manifest_sha` and `installed_by`, nil on a record from before it was kept); the manifest keys it records sit in one `mirror` Hash (`MIRROR_KEYS`, frozen: a key a later manifest gains is never mirrored) that only `InstalledManifest` reads, so manifest data is read through `#manifest`. `BundleHook#events` is the manifest's `event:` or its `events:` list (written back as `event:` for one, as an older chi reads it); `Hooks::BundleLoader#load_hook_file` builds the hook once and registers it under each |
|
|
51
57
|
| `BlockRenderers` (`lib/samagotchi/block_renderers.rb`) | a manifest's `block_renderers:`: `.parse_list`, and what chi web asks without loading plugins: `.for_installed` (first bundle by name wins a language; a record with an error, an unmet `requires_chi` or a list that doesn't parse is skipped) and `.script_path` (`load` membership first, then `Provenance.sha_matches?`). `Manifest` checks each `load` is a `scripts:` key; the files are ordinary scripts everywhere else ([block-renderers.md](block-renderers.md)) |
|
|
52
58
|
| `Builder` | `chi bundle build`: memories (+ the installed bundle's hooks, rules, plugin, scripts) → zip/tar/dir |
|
|
53
59
|
| `Profile` | meta bundles (`includes:`): install the members, record which; `InstallResult#kept_own` names a new member's memory files the user already had, with other bytes (`kept_own_line`, printed by `chi bundle`, `chi update` and `chi bootstrap`); a dry run runs each member's Installer as a dry run too, so it names them as well |
|
|
54
|
-
| `SystemBundle`, `ShippedUpdate` | the built-in system bundle (`ensure!` at Engine start, `sync` in `chi update`) and `chi update`'s upgrade of shipped bundles; both drive the Installer with `upgrade: true` and `Remerge` at an equal version (`ShippedUpdate::Row`'s `:would_merge`, `:merged`; `SystemBundle::Result`'s `:merged`). `SystemBundle` also puts back a memory missing at an equal version, that file alone, with no Installer (`verify_files_present`, `Provenance#record_restored`). `ensure!` echoes a merge in one line, in the row's words. That reaches a terminal launch; a worker has only its log (`Log.configure(stderr: false)`), so every merge leaves a note on the file's record (`Installer#note_merges`, a failed run's too, and `Remerge#note_merges`; `Provenance#note_merged`: version, time, trash dir). `chi bundle status` shows it, and `chi update` reads the notes before the run (`SystemBundle.merge_notes`, `ShippedUpdate::Row#earlier`), names them in the row and clears after it those exact ones and the notes of the files that run merged (a worker's meanwhile, on another file, stays), unless it was a dry run, the row failed or has no place for them (a skipped system row), or the run is unattended (`ChildEnv::UNATTENDED_UPDATE`, which `Web::UpdateOps` sets for `update.auto`, an env marker so a hand-over's exec keeps it and an older chi ignores it; `--json` alone says nothing, as a person or script may use it). `chi bundle upgrade` clears the notes of what it printed, and `--force` those of the files it replaced. `--json`'s `merged` is this run's, `earlier` the notes'. `ensure!` says what an upgrade left behind and nothing of a file already behind, which `Result#behind` still has for the row. A plan or sync that can't read a file is that bundle's failed row, in `Trash.failure_message`'s words (`ShippedUpdate.plan_row`, `SystemBundle.sync`). `ShippedUpdate.external` is the rest: what `chi update` only checks |
|
|
55
|
-
| `InstalledCopy`, `BundleCommand::InstalledCheck` | `chi update`'s re-check of the bundles chi didn't ship ([docs/cli.md](../cli.md#updating)). `InstalledCopy` lays a record and its installed files out as a source folder (manifest.yml from `Manifest.write`, the files' own shas, memories from `bases
|
|
60
|
+
| `SystemBundle`, `ShippedUpdate` | the built-in system bundle (`ensure!` at Engine start, `sync` in `chi update`) and `chi update`'s upgrade of shipped bundles; both drive the Installer with `upgrade: true` and `Remerge` at an equal version (`ShippedUpdate::Row`'s `:would_merge`, `:merged`; `SystemBundle::Result`'s `:merged`). `SystemBundle` also puts back a memory missing at an equal version, that file alone, with no Installer (`verify_files_present`, `Provenance#record_restored`). `ensure!` echoes a merge in one line, in the row's words. That reaches a terminal launch; a worker has only its log (`Log.configure(stderr: false)`), so every merge leaves a note on the file's record (`Installer#note_merges`, a failed run's too, and `Remerge#note_merges`; `Provenance#note_merged`: version, time, trash dir). `chi bundle status` shows it, and `chi update` reads the notes before the run (`SystemBundle.merge_notes`, `ShippedUpdate::Row#earlier`), names them in the row and clears after it those exact ones and the notes of the files that run merged (a worker's meanwhile, on another file, stays), unless it was a dry run, the row failed or has no place for them (a skipped system row), or the run is unattended (`ChildEnv::UNATTENDED_UPDATE`, which `Web::UpdateOps` sets for `update.auto`, an env marker so a hand-over's exec keeps it and an older chi ignores it; `--json` alone says nothing, as a person or script may use it). `chi bundle upgrade` clears the notes of what it printed, and `--force` those of the files it replaced. `--json`'s `merged` is this run's, `earlier` the notes'. `ensure!` says what an upgrade left behind and nothing of a file already behind, which `Result#behind` still has for the row. A plan or sync that can't read a file is that bundle's failed row, in `Trash.failure_message`'s words (`ShippedUpdate.plan_row`, `SystemBundle.sync`). At an equal version both keep the shipped `manifest.yml` for a record an older chi wrote (`Provenance#adopt_manifest` from the shipped dir): `SystemBundle.sync` under its lock, not in a dry run (so at the first Engine start or `chi update` after the upgrade, once), and `ShippedUpdate.apply` for a row that ends `:up_to_date` or `:merged` (`#keep_manifest`, through `ManifestStanding.keep`: `Row#manifest` is its line, `would keep its manifest` from the plan's dry run, appended to the row's status); a bumped bundle gets it from its Installer. `ShippedUpdate.external` is the rest: what `chi update` only checks |
|
|
61
|
+
| `InstalledCopy`, `BundleCommand::InstalledCheck` | `chi update`'s re-check of the bundles chi didn't ship ([docs/cli.md](../cli.md#updating)). `InstalledCopy` lays a record and its installed files out as a source folder (manifest.yml from `Manifest.write`, the files' own shas, memories from `bases/`; its `files:`, `hooks:`, `plugin:` and `scripts:` are always what the record and the copy hold, so a memory the install left out, one with no base or a manifest hook the record lacks gives no `missing-file`, and every other key is the kept `manifest.yml`'s when `InstalledManifest` read it, so the check sees its unknown keys, else the mirror's; a kept key that didn't parse keeps the value `InstalledManifest` fell back to, since `InstalledCheck` already names its problem); `InstalledCheck` forks per bundle (a plugin's `exit` or hang is that bundle's row; stdio to /dev/null, so `--json` stays parseable; `TIMEOUT`, then KILL of its group) and runs `Check.new(dir, installed: true)` there, which leaves out what only suits a source tree. Its `boundary` finding is narrower there than in a source tree: `Testing::Boundary.gone?` calls a reference gone only when its constant doesn't resolve or the object has no method the name names, so a bundle reaching `Samagotchi::Log` gets no row. lib loads lazily, so when a name doesn't resolve `gone?` requires every file of this chi's library in that same child and asks again (about 0.2 s, only for a bundle that reaches into chi at all); anything it can't read — a `const_get`, a require of a file that is there — is taken as still there. The fork is the point: `Sandbox` swaps ENV and the plugin's code is arbitrary. `UpdateCommand#external_rows` makes the rows (`level`, never `failed`) and holds the check back while a newer gem is yet to take over (`@not_checked`), since a fork is this chi's code |
|
|
56
62
|
| `Uninstaller`, `Trash`, `Status`, `Listing` | remove (the bundle's own folder too: an edited file there is copied to the trash first, `Uninstaller#trash_copy_line`); keep removed files (`Trash#move`) and a copy of overwritten ones (`Trash#copy`, its `as:` keeping a subfolder); `chi bundle status` (per file, `behind`, `merge_note`, `unreadable`; `issues=` counts behind, missing and unreadable, not edited); `chi bundle list` (`Installed#behind`) |
|
|
57
63
|
| `IndexUpdater`, `IndexSync` | the managed `index.md` lines (Installer; and the write/edit tools for memories) |
|
|
58
64
|
| `EntrySize` | one memory's size in tokens: `memory.entry_warn_tokens`' crossing note after a memory write, `chi self`'s `big memories` row |
|
|
@@ -70,7 +76,16 @@ and a normal install from replacing each other (this is also why `chi bundle upg
|
|
|
70
76
|
|
|
71
77
|
Readers of the installed state: `Hooks::BundleLoader` (Engine), `GuardrailWiring#bundle_rules`, `Plugin::Loader`,
|
|
72
78
|
`ContextProviders` (scripts), `BundleNeeds` (index markers). Each goes through `Provenance.each_installed` / `#record`
|
|
73
|
-
and checks the recorded sha256 (and `requires_chi`) before loading.
|
|
79
|
+
and checks the recorded sha256 (and `requires_chi`) before loading. `ContextProviders.resolve`/`.any?` skip a bundle
|
|
80
|
+
whose `requires_chi` this chi doesn't meet, logging `providers_too_new` once per process per bundle and version (a
|
|
81
|
+
Set under a Mutex, like `GuardrailWiring`'s kept-rules notice), and `.command_for` raises `Invalid` for a source
|
|
82
|
+
attached to one. They read shas and state off the record, and
|
|
83
|
+
manifest data (requires_chi, hook metadata, needs, providers, renderers, settings) through `InstalledBundle#manifest`
|
|
84
|
+
(`InstalledManifest`: the kept `manifest.yml` when its sha holds, else the record's mirror), as do `SessionConfig`, `BundleSettingsCheck`, `BlockRenderers`, `Status`, `BundleCommand`'s
|
|
85
|
+
status and diff, `Builder` and `InstalledCopy`. Their requires_chi checks are `InstalledManifest#requires_chi_failure`;
|
|
86
|
+
`Hooks::BundleLoader.load` is given the installed bundle's manifest and uses the same gate. `GuardrailWiring#rules_stamp` stats each bundle's
|
|
87
|
+
`manifest.yml` with its `manifest.json` and `guardrails/*`, so a changed requires_chi re-reads the rules. No reader
|
|
88
|
+
calls `Manifest.new` on a kept file: `Manifest.tolerant` through `InstalledManifest` is the one read.
|
|
74
89
|
The checks live in `Hooks::BundleLoader.load` and `Plugin::Loader.load_bundle`; the loading itself is
|
|
75
90
|
`BundleLoader.load_hook_file` (one hook file, into a given `namespace:`) and `Plugin::Loader.register_file` (one
|
|
76
91
|
plugin file: build, `register`, `commit!`), which raise instead of logging: the bundle test kit
|
|
@@ -81,12 +96,23 @@ spec's shape). A load failure carries `LoadFailures::Failure#location` (the firs
|
|
|
81
96
|
bundle, or a SyntaxError's own line), which `Install` maps from the installed copy (`.bundles/<name>/plugin/`,
|
|
82
97
|
`hooks/`) back to the source dir.
|
|
83
98
|
|
|
99
|
+
Records from older chi. A record written before the kept manifest (**legacy**: no `manifest.yml`, no
|
|
100
|
+
`manifest_sha`), or rewritten since by such a chi (**stale**: a kept file, no `manifest_sha`; its file may be an
|
|
101
|
+
earlier version's), or whose kept file is gone (**missing**) reads the record's mirror: exactly what that install
|
|
102
|
+
recorded, so nothing breaks, and a key the installing chi didn't know stays unread. The mirror stays written because
|
|
103
|
+
those older chis (an old worker, an old chi web, an old gem, another worktree or chi home) keep reading and writing
|
|
104
|
+
`.bundles/` with no time limit; without its `requires_chi` they would load a too-new bundle. The gap closes where chi
|
|
105
|
+
has the bundle's own manifest at the recorded version (`Provenance#adopt_manifest`): `SystemBundle.sync` and
|
|
106
|
+
`ShippedUpdate.apply` from the shipped dir, `chi update` from an external record's folder or archive
|
|
107
|
+
(`ManifestStanding.keep`). A git source is never cloned for it; `chi bundle upgrade NAME` reads it again.
|
|
108
|
+
`ManifestStanding` says which case a record is in, and the command, for `chi update` and `chi bundle status NAME`.
|
|
109
|
+
|
|
84
110
|
## Installer#run, in order
|
|
85
111
|
|
|
86
112
|
The order matters: warnings print in it, and a plugin/script the manifest names but the bundle lacks raises
|
|
87
113
|
`InstallError` after the memories are already copied.
|
|
88
114
|
|
|
89
|
-
1. `load_source`: normalize (or take the `PreparedSource` `BundleCommand` passed as `prepared:`), read `manifest.yml` (strict: a missing/invalid one raises; non-strict: a warning).
|
|
115
|
+
1. `load_source`: normalize (or take the `PreparedSource` `BundleCommand` passed as `prepared:`), read `manifest.yml` (strict: a missing/invalid one raises; non-strict: a warning). Then `refuse_too_new`: a bundle whose `requires_chi` this chi doesn't meet raises `InstallError` unless `force` — nothing is written, dry run included (docs/plugins.md, "Loading, and when it fails").
|
|
90
116
|
2. `resolve_target`: scope = CLI > manifest > `system`; then `check_source` against the existing record
|
|
91
117
|
(`SourceIdentity`, source.rb): a name differing from an installed one only in case raises (`force` too); a
|
|
92
118
|
shipped name (any case) from a source that isn't a dir in the Installer's `shipped_dir:` (default `SHIPPED_DIR`;
|
|
@@ -129,7 +155,13 @@ doesn't own: the Installer's `MemoryFiles.select`; `ShippedUpdate.forecast`' eve
|
|
|
129
155
|
can name a file dropped from `files:` or now a symlink, which the upgrade doesn't merge: edited, it stays (`kept_pruned`, or `kept_shared` when another bundle has it too); `SystemBundle`'s dry
|
|
130
156
|
run the manifest's `files:`, so a manifest with none plans nothing (the shipped one lists its files).
|
|
131
157
|
|
|
132
|
-
Results are `@results` (file → `{status:, reason:}`), summarised by `#summary
|
|
158
|
+
Results are `@results` (file → `{status:, reason:}`), summarised by `#summary`: a file that already holds the
|
|
159
|
+
bundle's bytes (`reason: "already up to date"`, a memory or a hook/rule/plugin/script) is listed as `Up to date:`,
|
|
160
|
+
and only the files really left out (`already exists`, a dry run's `would_skip`, a dry run upgrade's `noop`) as
|
|
161
|
+
`Skipped:`. A plain install over a bundle already installed from the same source ends with `NAME was already
|
|
162
|
+
installed from SOURCE: its hooks, rules, plugin and scripts now match it and its record was rewritten`, plus
|
|
163
|
+
`; your own copies of its memories were kept (chi bundle upgrade NAME merges the bundle's changes into them)` when
|
|
164
|
+
a memory was left out as `already exists`. An upgrade with conflicts keeps an
|
|
133
165
|
owned source dir alive (`#source_dir`) until `#cleanup_source!`, because the conflict agent reads the incoming
|
|
134
166
|
files; `BundleCommand#settle_conflicts` then runs `Provenance#resolve_conflicts`, which moves a base forward and keeps
|
|
135
167
|
the entry's description, only for a file the agent step changed that holds no markers (`Merger.markers?`), and whose incoming
|
|
@@ -140,5 +172,7 @@ copy is still there. Any other keeps its old base and mark, and the command exit
|
|
|
140
172
|
`select_memories` (all but `index.md`/hidden; with `--files` the named ones + their overlays, else leaving out files
|
|
141
173
|
another installed bundle owns) → `checksum_memories` → `installed_parts` (`Builder::InstalledParts`: the same-name
|
|
142
174
|
installed bundle's hooks, rules, plugin, scripts, context providers, else the scope dir's `hooks/`/`guardrails/`) →
|
|
143
|
-
`stage_files` + `write_manifest`
|
|
175
|
+
`stage_files` + `write_manifest` (requires_chi, needs, settings… from the installed bundle's `InstalledManifest`;
|
|
176
|
+
`trust_level` and `description` the CLI's, else that manifest's; a kept manifest's unknown keys carried, each with a
|
|
177
|
+
`unknown_key_line` warning) in a temp dir → `write_out` (zip, tar, tgz, or a new/empty dir). An installed
|
|
144
178
|
profile's name is refused (`refuse_profile!`): a build can't remake its includes.
|
|
@@ -116,10 +116,12 @@ Bridge's `TurnAccumulator#current_messages`) leaves tool calls out. A move shows
|
|
|
116
116
|
|
|
117
117
|
`action: propose` (`change` add, reword, done or drop; `id`; `text`; `note`, why) checks the change against the list
|
|
118
118
|
(`Objectives::Proposal#refusal`) and hands it to `Engine#propose_objective`, which keeps it until the turn is over,
|
|
119
|
-
as `#context_choice`'s cards are kept
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
119
|
+
as `#context_choice`'s cards are kept. `run_turn`'s ensure shows them only for a turn that completed: a canceled or
|
|
120
|
+
failed one drops them (`Engine#discard_objective_proposals`), so its Apply card never outlives a turn whose call a
|
|
121
|
+
rollback has taken out of the history, and nothing carries over to the next turn. A card is between turns (no
|
|
122
|
+
`in_turn`), so it writes no `pending_card.json`, the stage doesn't say it waits, and it keeps its buttons over a
|
|
123
|
+
restart (`CardStore` takes them only from a card shown in a turn). The same change twice in a turn is one card. A P3
|
|
124
|
+
finding stands: a card action can't carry a plain reply, so an action here is a real edit.
|
|
123
125
|
|
|
124
126
|
Apply is `/objectives --card ID done O2`: the proposal's own line (`Proposal#line`) behind a prefix the command reads
|
|
125
127
|
(`ObjectivesCommand::CARD`), so it goes through the one route, with its refusals. On success the card with that id is
|
data/docs/memory.md
CHANGED
|
@@ -539,7 +539,7 @@ for a file another bundle installed.
|
|
|
539
539
|
tell whether the file holds edits of yours and says that; the file
|
|
540
540
|
stays behind until you resolve it by hand, with `--agent` or with `--force`, and
|
|
541
541
|
the retry can't fix that. A file left behind makes the upgrade
|
|
542
|
-
exit 2; `chi bundle diff NAME FILE` shows the base, and `--force` takes
|
|
542
|
+
exit 2; `chi bundle diff NAME FILE` shows the base, your file and the bundle's new text, and `--force` takes
|
|
543
543
|
the bundle's version, copying a file that differs to the bundle trash first
|
|
544
544
|
(the output says `Copied to the trash first: …`).
|
|
545
545
|
- **Every merge is noted on the file's record.** The system bundle's upgrade and
|
|
@@ -553,6 +553,11 @@ for a file another bundle installed.
|
|
|
553
553
|
behind, missing or unreadable (and a plugin or hooks that won't load), not edits. A
|
|
554
554
|
file an older chi marked, which no retry has looked at, says `may be behind`, and so
|
|
555
555
|
do the counts in `chi bundle list`, `chi self` and `chi update`'s last line.
|
|
556
|
+
`chi bundle status NAME` also has a `Manifest:` line: `[kept]` when chi reads
|
|
557
|
+
the bundle's installed `manifest.yml`, `[from the install record]` when it reads
|
|
558
|
+
what the install recorded, with why (installed by an older chi, the file edited
|
|
559
|
+
or gone) and the command that fixes it (`chi update` keeps it where it can,
|
|
560
|
+
[CLI: Updating](cli.md#updating)).
|
|
556
561
|
- **A dev install** (`chi bundle install --dev PATH`) links the hooks, rules,
|
|
557
562
|
plugin and scripts to `PATH` but still copies the memories at install, so
|
|
558
563
|
an edit to one of them in `PATH` isn't seen until the next install.
|
data/docs/plugins.md
CHANGED
|
@@ -29,6 +29,7 @@ plugin:
|
|
|
29
29
|
file: plugin.rb
|
|
30
30
|
sha256: sha256:6a1a7022… # shasum -a 256 plugin.rb
|
|
31
31
|
requires_chi: ">= 0.1.28" # optional: a gem-style requirement (">= 0.1.28, < 0.2"); the bundle's hooks/ check it too
|
|
32
|
+
# every key below has a chi floor: check warns when requires_chi admits a chi that never read it
|
|
32
33
|
# requires_chi_next: true # shipped bundles only, ignored by chi: rake release:bump raises requires_chi
|
|
33
34
|
# to the chi being released (docs/releasing.md)
|
|
34
35
|
session_settings: [cap] # optional: the settings a session may set (chi --set my-bundle.cap=3);
|
|
@@ -1351,6 +1352,10 @@ context_providers:
|
|
|
1351
1352
|
and becomes the installed bundle's folder each time it runs, so an
|
|
1352
1353
|
upgrade moves nothing. `{ruby}` becomes the Ruby chi runs on (a `ruby` on
|
|
1353
1354
|
the PATH may be another one, such as macOS's 2.6).
|
|
1355
|
+
- A bundle whose `requires_chi` this chi doesn't meet provides nothing:
|
|
1356
|
+
`.resolve` and `.any?` skip it (logged once per process per bundle and
|
|
1357
|
+
version), and a source already attached to one — before a chi downgrade,
|
|
1358
|
+
say — is refused by name, as a changed script is.
|
|
1354
1359
|
- The command follows [the contract](context.md#the-command-contract).
|
|
1355
1360
|
|
|
1356
1361
|
## Block renderers
|
|
@@ -1390,9 +1395,9 @@ block_renderers:
|
|
|
1390
1395
|
|
|
1391
1396
|
## Stable for bundles
|
|
1392
1397
|
|
|
1393
|
-
Bundles normally use only `chi.*` and `ctx`; the constants and
|
|
1394
|
-
are the rest of the surface they may use. A change to any of them gets
|
|
1395
|
-
CHANGELOG line and bumps the shipped bundles that use it. Anything else under
|
|
1398
|
+
Bundles normally use only `chi.*` and `ctx`; the constants, helpers and manifest
|
|
1399
|
+
keys below are the rest of the surface they may use. A change to any of them gets
|
|
1400
|
+
a CHANGELOG line and bumps the shipped bundles that use it. Anything else under
|
|
1396
1401
|
`Samagotchi::` is internal.
|
|
1397
1402
|
|
|
1398
1403
|
**New API ships with its first caller:** a `chi.*` or `ctx.*` method arrives in the same change as a shipped bundle
|
|
@@ -1424,8 +1429,23 @@ that calls it, or for a real external bundle that does. No method on the chance
|
|
|
1424
1429
|
`Samagotchi::VERSION`, `Samagotchi::CancellationController` (`new`, `cancel!`: what `cancel:` takes) and
|
|
1425
1430
|
`Samagotchi::GitEnv::UNSET` (a spawn env: a bundle's own git, or gh, asks about the folder it names, not the repo
|
|
1426
1431
|
a hook's or `rebase --exec`'s environment names).
|
|
1427
|
-
|
|
1428
|
-
|
|
1432
|
+
7. **The manifest schema**: the manifest's keys and shapes, as [A bundle with a plugin](#a-bundle-with-a-plugin)
|
|
1433
|
+
and [bundles.md](bundles.md) document them — `name`, `version`, `scope`, `description`, `files`, `hooks`,
|
|
1434
|
+
`trust_level`, `plugin`, `requires_chi`, `requires_chi_next` (shipped bundles only: the release tooling
|
|
1435
|
+
reads it, chi doesn't), `needs`, `scripts`, `context_providers`, `block_renderers`, `includes`,
|
|
1436
|
+
`session_settings`, `settings`. Those are `Manifest::KEYS`; `Manifest::SINCE` is the chi release each
|
|
1437
|
+
key or shape first worked in, and the docs don't copy it — the code is its truth.
|
|
1438
|
+
- A key keeps its meaning. A new key gets a `SINCE` entry and a CHANGELOG line, and an older chi says
|
|
1439
|
+
it ignores it (`unknown-key`).
|
|
1440
|
+
- A later chi reads every key an earlier chi installed: the install keeps the bundle's `manifest.yml`
|
|
1441
|
+
([Trust](#trust)). One installed before chi kept manifests reads what its install recorded until it is
|
|
1442
|
+
upgraded.
|
|
1443
|
+
- A bundle declares its floor with `requires_chi`, and an install below it is refused (`--force` aside).
|
|
1444
|
+
There is no `api_version:`.
|
|
1445
|
+
- `block_renderers:` and the frame's `chi.defineRenderer` ([Block renderers](#block-renderers)) are part
|
|
1446
|
+
of it. Neither is Ruby, so the scan below doesn't see them.
|
|
1447
|
+
|
|
1448
|
+
`Samagotchi::Testing::Boundary::ALLOWED` is the checked copy of items 1-6,
|
|
1429
1449
|
and `Boundary::EXCEPTIONS` the per-bundle list of what each shipped bundle
|
|
1430
1450
|
(and its specs) reaches beyond it, each with why: the APIs to design before
|
|
1431
1451
|
that bundle could leave chi's repo. `spec/samagotchi/testing/boundary_spec.rb`
|
|
@@ -1433,9 +1453,6 @@ fails on a reference in neither (docs/testing.md). In `chi update`'s re-check of
|
|
|
1433
1453
|
an installed bundle the same scan warns only for a name this chi no longer has
|
|
1434
1454
|
(`Boundary.gone?`): a reference that still resolves gets no row.
|
|
1435
1455
|
|
|
1436
|
-
A bundle's `block_renderers:` and the frame's `chi.defineRenderer` ([Block renderers](#block-renderers)) are stable
|
|
1437
|
-
too. They aren't Ruby, so the scan above doesn't see them.
|
|
1438
|
-
|
|
1439
1456
|
**Used by shipped bundles today, but internal** (may change without notice; a
|
|
1440
1457
|
bundle that needs one should ask for an API instead):
|
|
1441
1458
|
|
|
@@ -1551,7 +1568,8 @@ docs/testing.md). **Minitest:** `require "samagotchi/testing/minitest"` and subc
|
|
|
1551
1568
|
`Samagotchi::Testing::Case` (the same helpers).
|
|
1552
1569
|
- **Installed, not loaded from source:** `Samagotchi::Testing::Install.run(dir, settings: {})` (or
|
|
1553
1570
|
`bundle.install`) installs the bundle into the sandbox as `chi bundle install` does (sha256 and `requires_chi`
|
|
1554
|
-
checked
|
|
1571
|
+
checked: a bundle whose `requires_chi` this chi doesn't meet raises `InstallError` unless `force: true`)
|
|
1572
|
+
and loads the installed copy with chi's own loaders. It returns `warnings` (the Installer's),
|
|
1555
1573
|
`plugin_failures` / `hook_failures` (each with the `location` in your source, `plugin.rb:12`) and `registered`
|
|
1556
1574
|
(commands, tools, hooks per event, inits, toolkits, tool and peer sources). A name chi ships raises
|
|
1557
1575
|
`MemoryBundle::Installer::InstallError` unless `force: true`.
|
|
@@ -1579,7 +1597,9 @@ Plugins load when a session starts (`Engine.new`, in the REPL or a
|
|
|
1579
1597
|
worker), after the bundle hooks. The steps are:
|
|
1580
1598
|
|
|
1581
1599
|
1. The file must match the sha256 recorded at install (not checked for a bundle installed with `--dev`).
|
|
1582
|
-
2. chi must meet `requires_chi`.
|
|
1600
|
+
2. chi must meet `requires_chi`. A bundle whose `requires_chi` this chi doesn't
|
|
1601
|
+
meet is refused at install, so this step is reached only by one installed
|
|
1602
|
+
with `--force` or by a chi that has since been downgraded.
|
|
1583
1603
|
3. The file is `module_eval`'d into a new module in the bundle's namespace
|
|
1584
1604
|
(`Samagotchi::Bundles::<bundle>`).
|
|
1585
1605
|
4. The class is built, and `register(chi)` runs.
|
|
@@ -1609,6 +1629,11 @@ gem.
|
|
|
1609
1629
|
source directory's, and an edit loads at the next start.
|
|
1610
1630
|
- Install only copies the file (`--dev` links it). The code first runs at the
|
|
1611
1631
|
next session start.
|
|
1632
|
+
- The bundle's `manifest.yml` is kept at install and checked the same way: chi
|
|
1633
|
+
reads hook events, `context_providers:`, `block_renderers:`, settings and
|
|
1634
|
+
`requires_chi` from it only while it has the sha256 recorded at install. An
|
|
1635
|
+
edited one isn't read (chi falls back to what the install recorded). `--dev`
|
|
1636
|
+
links it, unchecked.
|
|
1612
1637
|
- Guardrail rules keyed by a tool's name apply to plugin tools, as they do to
|
|
1613
1638
|
chi's own tools; path and command rules see what `targets:` says.
|
|
1614
1639
|
|
|
@@ -1616,18 +1641,30 @@ gem.
|
|
|
1616
1641
|
|
|
1617
1642
|
- `chi bundle install <dir>`: copies the plugin to
|
|
1618
1643
|
`<memories>/.bundles/<name>/plugin/`. It records the plugin's sha256 and
|
|
1619
|
-
`requires_chi`, and warns about a wrong declared sha256
|
|
1620
|
-
`requires_chi
|
|
1644
|
+
`requires_chi`, and warns about a wrong declared sha256. A bundle whose
|
|
1645
|
+
`requires_chi` this chi doesn't meet is **refused**: `Install failed: NAME
|
|
1646
|
+
can't be installed: it requires chi >= X (this is chi Y)`, exit 1, nothing
|
|
1647
|
+
written. `--force` installs it anyway, with the warnings that its plugin,
|
|
1648
|
+
hooks and rules won't load. It keeps the bundle's `manifest.yml` beside the record, and
|
|
1649
|
+
chi reads the manifest from there at load, so for a bundle installed by this
|
|
1650
|
+
chi or later, a key this chi didn't know at install works once a newer chi
|
|
1651
|
+
reads it (one installed before reads what its install recorded until
|
|
1652
|
+
`chi bundle upgrade <name>`).
|
|
1653
|
+
- `chi bundle upgrade <name>`: upgrades an installed bundle by name from the
|
|
1654
|
+
source its record keeps (its folder, archive or git URL, `#ref` included),
|
|
1655
|
+
so a bundle from elsewhere needs no path retyped; `upgrade <source>` takes a
|
|
1656
|
+
new one and says `Source changes: old → new`. A dev install is refused: it
|
|
1657
|
+
already follows its source.
|
|
1621
1658
|
- `chi bundle status [<name>]`: shows `plugin=plugin.rb` in the list. For
|
|
1622
1659
|
one bundle it shows `Plugin: plugin.rb [ok|modified|missing]` and a
|
|
1623
1660
|
`requires_chi` failure.
|
|
1624
1661
|
- `chi bundle diff <name> [plugin.rb]`: shows the installed base and the
|
|
1625
|
-
file on disk.
|
|
1662
|
+
file on disk; for a memory file that stays behind, also the bundle's new text.
|
|
1626
1663
|
- `chi bundle build --name <name>`: puts the installed plugin (and
|
|
1627
1664
|
`requires_chi`, its `scripts/` and `context_providers:`) into the built
|
|
1628
1665
|
bundle.
|
|
1629
1666
|
- `chi bundle uninstall <name>`: removes the plugin with the bundle.
|
|
1630
|
-
- `chi bundle install --dev <dir>`: links the bundle's plugin, hooks, rules and
|
|
1667
|
+
- `chi bundle install --dev <dir>`: links the bundle's plugin, hooks, rules, scripts and `manifest.yml` to `<dir>` instead of
|
|
1631
1668
|
copying them, with no sha256 check, so an edit applies at the next worker start
|
|
1632
1669
|
([cli.md](cli.md#writing-a-bundle-new-test-check)).
|
|
1633
1670
|
- `chi bundle new`, `test` and `check`: start a bundle, run its tests on chi's Ruby, check it as an install takes
|