|
1 | 1 | // SPDX-License-Identifier: PMPL-1.0-or-later |
| 2 | +// SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk> |
2 | 3 | = Modshells: Modular Shell Configuration Manager — Show Me The Receipts |
3 | 4 | :toc: |
4 | 5 | :icons: font |
5 | 6 |
|
6 | | -The README makes claims. This file backs them up. |
| 7 | +The README makes claims. This file backs them up. For each headline feature: |
| 8 | +what makes it work, where the code is, and an honest caveat. |
| 9 | + |
| 10 | +== Claim: Idempotent modularisation across ten shells |
7 | 11 |
|
8 | 12 | [quote, README] |
9 | 13 | ____ |
10 | | -This project must declare **MPL-2.0-or-later** for platform/tooling compatibility. |
| 14 | +All Modshells operations are idempotent: running the tool multiple times |
| 15 | +produces the same result. Existing configurations are never destroyed. |
| 16 | +Automatic backup before any modification. |
11 | 17 | ____ |
12 | 18 |
|
13 | | -== File Map |
| 19 | +Idempotency works through signature-based detection. Before injecting any |
| 20 | +sourcing block, `Shell_Manager.Modularise_Shell` calls `Is_Already_Modularised` |
| 21 | +which scans the target config file for the string `MODSHELLS_START`. If found, |
| 22 | +the shell is skipped entirely. If absent, the procedure creates a timestamped |
| 23 | +backup (`<path>.modshells-backup-YYYYMMDD-HHMMSS`) then appends the |
| 24 | +shell-specific sourcing block delimited by `MODSHELLS_START` / `MODSHELLS_END` |
| 25 | +markers. The entry point `Modshells` in `src/main/modshells.adb` calls |
| 26 | +`Shell_Manager.Modularise_All_Shells` which iterates `Detect_Shells` and |
| 27 | +applies this pattern to every installed shell in a single pass. |
| 28 | + |
| 29 | +**Caveat:** The signature check is text-based. A user who manually edits |
| 30 | +the markers or copies config files between machines could confuse the check. |
| 31 | +There is no cryptographic protection on the injected block; it trusts the |
| 32 | +filesystem. |
| 33 | + |
| 34 | +- Implementation: `src/shell_manager/shell_manager.adb` — `Modularise_Shell`, |
| 35 | + `Is_Already_Modularised`, `Create_Backup`, `Get_Sourcing_Block` |
| 36 | +- Interface spec: `src/shell_manager/shell_manager.ads` |
| 37 | + |
| 38 | +== Claim: Written in Ada for safety-critical reliability |
| 39 | + |
| 40 | +[quote, README] |
| 41 | +____ |
| 42 | +Built in Ada for maximum reliability: strong static typing catches errors at |
| 43 | +compile time, exception handling ensures graceful failure, no unsafe memory |
| 44 | +operations, deterministic behaviour. |
| 45 | +____ |
| 46 | + |
| 47 | +The entire core is Ada 2012, compiled with GPRBuild via `modshells.gpr`. The |
| 48 | +package hierarchy separates concerns cleanly: `Config_Store` resolves paths |
| 49 | +(reads `MODSHELLS_CONFIG_PATH` or defaults to `~/.config/nushell/modshells`), |
| 50 | +`Shell_Manager` owns all shell-specific knowledge, and `Shell_Validator` |
| 51 | +provides input guards. Ada's discriminant-constrained `Shell_List` array type |
| 52 | +(declared in `shell_manager.ads`) prevents buffer overruns by construction. |
| 53 | +The top-level `exception when others => raise` in `modshells.adb` ensures |
| 54 | +that any unhandled error surfaces rather than silently failing. |
| 55 | + |
| 56 | +**Caveat:** The Ada runtime exception handler re-raises, so the binary will |
| 57 | +exit with a non-zero code on failure but the error message is plain text — |
| 58 | +there is no structured error output yet. Unit tests are in |
| 59 | +`tests/unit/test_shell_manager.adb` but the test suite is smoke-test-level |
| 60 | +rather than exhaustive at v0.1. |
| 61 | + |
| 62 | +- Project file: `modshells.gpr` |
| 63 | +- Entry point: `src/main/modshells.adb` |
| 64 | +- Package spec: `src/shell_manager/shell_manager.ads` |
| 65 | +- Tests: `tests/smoke_test.sh`, `tests/unit/` |
| 66 | + |
| 67 | +== Dogfooded Across The Account |
14 | 68 |
|
15 | 69 | [cols="1,2"] |
16 | 70 | |=== |
| 71 | +| Technology | Also Used In |
| 72 | + |
| 73 | +| **Ada / GPRBuild** | https://github.com/hyperpolymath/modshells[modshells] is the primary |
| 74 | + Ada CLI in the account; Ada is also used in safety-critical FFI layers across |
| 75 | + repos that require GNAT ecosystem tools |
| 76 | +| **POSIX shell config patterns** | Used as the human-readable layer in |
| 77 | + https://github.com/hyperpolymath/developer-ecosystem[developer-ecosystem] |
| 78 | + shell tooling and https://github.com/hyperpolymath/ambientops[ambientops] |
| 79 | + personal sysadmin scripts |
| 80 | +| **Guix + Nix packaging** | `guix.scm` / `flake.nix` present in all RSR repos; |
| 81 | + see https://github.com/hyperpolymath/proof-of-work[proof-of-work], |
| 82 | + https://github.com/hyperpolymath/burble[burble], and dozens of others |
| 83 | +|=== |
| 84 | + |
| 85 | +== File Map |
| 86 | + |
| 87 | +[cols="1,3"] |
| 88 | +|=== |
17 | 89 | | Path | What's There |
18 | 90 |
|
19 | | -| `src/` | Source code |
20 | | -| `test(s)/` | Test suite |
| 91 | +| `src/main/modshells.adb` |
| 92 | +| Entry point. Three-step procedure: create directories, detect shells, |
| 93 | + modularise all installed shells. Exception handler at top level. |
| 94 | + |
| 95 | +| `src/shell_manager/shell_manager.ads` |
| 96 | +| Public interface spec. Declares `Shell_Type` (10-member enum), `Shell_Status`, |
| 97 | + `Shell_Info` record, `Shell_List` unconstrained array, and all function |
| 98 | + signatures used by the main procedure. |
| 99 | + |
| 100 | +| `src/shell_manager/shell_manager.adb` |
| 101 | +| Implementation. Contains `Is_Shell_Installed` (checks `/usr/bin`, `/bin`, |
| 102 | + `/usr/local/bin`, `/opt/homebrew/bin`), `Get_Sourcing_Block` (generates |
| 103 | + shell-specific syntax for POSIX, Fish, Nushell, Tcsh, Ion, PowerShell), |
| 104 | + `Create_Backup`, `Is_Already_Modularised`, `Modularise_Shell`, and |
| 105 | + `Modularise_All_Shells`. |
| 106 | + |
| 107 | +| `src/config_store/config_store.adb` |
| 108 | +| Resolves the modshells root path. Reads `MODSHELLS_CONFIG_PATH` environment |
| 109 | + variable; falls back to `~/.config/nushell/modshells`. |
| 110 | + |
| 111 | +| `src/shell_validator/` |
| 112 | +| Input guards (separate from shell_manager to avoid circular dependencies). |
| 113 | + |
| 114 | +| `tests/smoke_test.sh` |
| 115 | +| Shell-based smoke test: checks source files exist, SPDX headers present, |
| 116 | + binary runs, directory creation works, idempotency check passes. |
| 117 | + |
| 118 | +| `tests/unit/test_shell_manager.adb` |
| 119 | +| Ada unit tests for Shell_Manager functions. Built separately via `tests/tests.gpr`. |
| 120 | + |
| 121 | +| `tests/e2e/` |
| 122 | +| End-to-end tests against a live shell environment. |
| 123 | + |
| 124 | +| `tests/property/` |
| 125 | +| Property-based tests (QuickCheck style). |
| 126 | + |
| 127 | +| `examples/` |
| 128 | +| Ready-to-use config snippets: `core/00-path.sh`, `tools/git.sh`, `tools/fzf.sh`, |
| 129 | + `misc/aliases.sh`, `os/linux.sh`, `ui/colours.sh`, and others. |
| 130 | + |
| 131 | +| `modshells.gpr` |
| 132 | +| GPRBuild project file. Source directories, object output path (`obj/`), |
| 133 | + binary output path (`bin/`). |
| 134 | + |
| 135 | +| `guix.scm` / `flake.nix` |
| 136 | +| Package manager manifests (primary: Guix; fallback: Nix). |
| 137 | + |
| 138 | +| `.machine_readable/6a2/` |
| 139 | +| A2ML checkpoint files: STATE, META, ECOSYSTEM, AGENTIC, NEUROSYM, PLAYBOOK. |
21 | 140 | |=== |
22 | 141 |
|
23 | | -== Questions? |
| 142 | +== Checking It |
| 143 | + |
| 144 | +[source,bash] |
| 145 | +---- |
| 146 | +# Build |
| 147 | +gprbuild -p -j0 modshells.gpr |
| 148 | +
|
| 149 | +# Smoke test (does not require build) |
| 150 | +./tests/smoke_test.sh |
| 151 | +
|
| 152 | +# Unit tests |
| 153 | +gprbuild -p -j0 -P tests/tests.gpr |
| 154 | +./bin/test_shell_manager |
24 | 155 |
|
25 | | -Open an issue or reach out directly — happy to explain anything in more detail. |
| 156 | +# Dry-run (inspect what would be changed) |
| 157 | +MODSHELLS_CONFIG_PATH=/tmp/modshells-test ./bin/modshells |
| 158 | +---- |
0 commit comments