Decision 0015: operator facing commands carry the Keel name #3

Merged
marcos-mendez merged 3 commits from docs/decision-0015-keel-named-commands into main 2026-09-29 00:43:03 -03:00
marcos-mendez commented 2026-09-28 00:07:34 -03:00 (Migrated from github.com)

Cross-reference only, nothing is closed from here: Keel-Linux/tracker#12 is the org-wide issue, and the implementation lives in its own repository issue, Keel-Linux/keel-wordpress#7, with Keel-Linux/keel-wordpress#6 as its pull request. The handbook carries the policy and nothing else.

Merge this before Keel-Linux/keel-wordpress#6, which cites decision 0015 in its changelog, COVERAGE.md, tests/wrappers.bats and both script headers.

docs/decisions/0015-operator-command-names.md records the rule the maintainer decided on 2026-09-28: the Keel name is the real command, the turnkey-* name is kept as a symlink to it, and both keep working. It is a general policy, not a per-appliance choice, and the reason for keeping the old name is the same one that keeps /etc/turnkey_version in 0014 — compatibility with TurnKey appliances is a stated property of this project (0008).

Responding to the review

HIGH — the inventory contradicted the note's own scope rule. Correct, and it did so in the direction that makes a contributor under-apply the policy. The rule covers "a script installed by a package this project builds"; the table listed only the two keel-wordpress overlay files, the two in common, and the foreign packages going to tracker#13. So the note handled somebody else's packages and silently dropped the ones we build.

I verified the five myself against the dpkg file lists and against the forks' own trees, and added them:

Command Package Source
/usr/sbin/turnkey-init inithooks 2.3.6+keel4 Keel-Linux/inithooks, turnkey-init
/usr/sbin/turnkey-sudoadmin inithooks 2.3.6+keel4 Keel-Linux/inithooks, turnkey-sudoadmin
/usr/sbin/turnkey-install-security-updates inithooks 2.3.6+keel4 Keel-Linux/inithooks, turnkey-install-security-updates
/usr/lib/inithooks/bin/turnkey-init-fence inithooks 2.3.6+keel4 Keel-Linux/inithooks, bin/turnkey-init-fence
/usr/bin/turnkey-lexicon confconsole 2.2.3+keel2 Keel-Linux/confconsole, turnkey-lexicon

Also added, from LOW 1: turnkey-artisan and turnkey-composer, tracked 100755 files in common reached through laravel.mk and composer.mk; and the byte-identical second copy of turnkey-mysql-install-perf-info-schemas in unit-mariadb (same blob c8f185b2), which the old table attributed to common alone, so anybody following it would have renamed one of two copies. turnkey-make-ssl-cert from turnkey-ssl 3.1.1 is now listed too, explicitly out of scope, so the next reader does not have to derive that again.

The section is restructured: fifteen distinct names grouped by what puts the file on the machine — appliance overlay, common overlay, a package we build, a package somebody else builds — because that is what decides when each one can move. Eleven are ours. turnkey-init-fence is marked as not on PATH and not a command an operator types, with the observation that the same name is also a conffile, a systemd unit and two firstboot hooks, which is a packaging question the note does not settle. And the section now closes by saying that when the table and the rule disagree, the rule wins — a table is a measurement of one day and this one has been corrected three times.

I took the reviewer's framing on the +keel suffix: the rule reads on "a package this project builds", and turnkey-sysinfo 1.1.0, turnkey-version 1.1.3 and turnkey-ssl 3.1.1 carry no such suffix while inithooks and confconsole do. The "what a package owns does instead" section now opens by saying it is about packages we do not build, since getting that the wrong way round is precisely the mistake the first version made.

MEDIUM — the -d claim. Correct, and worse in a note than in code, because a note is applied without asking. Measured with coreutils 9.7 and written into the note as a table:

cp -TR   →  link -> real     symlink kept, hard link split (nlink 1)
cp -TdR  →  link -> real     symlink kept, hard link kept  (nlink 2)
cp -TLR  →  link             a second regular file — this one flattens

The property is the absence of -L, not the presence of -d. keel-wordpress#6 now has a test asserting all three rows, so the claim is checked rather than written down.

LOW 2 — the Decision section now states that the link is relative and why an absolute one breaks the build-time caller, and that the Keel name is the turnkey- prefix replaced by keel- and nothing else. LOW 3 — "must be covered", and it names who is owed those tests. LOW 4 — the reverse-dependency provenance says the container throughout. LOW 5 — merge order is stated at the top of both pull request descriptions.

Test plan

  • The number is 0015 and only 0015; 0014 is being written concurrently and is untouched here.
  • The note follows the form of 0001-0013: title line, Date, Status, ## Decision, the three-part justification of brief section 10, ## Consequences.
  • Every added row verified against dpkg -L/dpkg -S file lists, git ls-files across the organization's repositories, and the .mk files that pull each overlay.
  • Maintainer confirms the note matches the decision taken on 2026-09-28, and in particular the reviewer's reading that "a package this project builds" means a package carrying a +keel version.

This repository ships no package and has no package / changelog gate, so there is no changelog entry to add, and no checks are reported on the branch.

Cross-reference only, nothing is closed from here: Keel-Linux/tracker#12 is the org-wide issue, and the implementation lives in its own repository issue, Keel-Linux/keel-wordpress#7, with Keel-Linux/keel-wordpress#6 as its pull request. The handbook carries the policy and nothing else. **Merge this before Keel-Linux/keel-wordpress#6**, which cites decision 0015 in its `changelog`, `COVERAGE.md`, `tests/wrappers.bats` and both script headers. `docs/decisions/0015-operator-command-names.md` records the rule the maintainer decided on 2026-09-28: the Keel name is the real command, the `turnkey-*` name is kept as a symlink to it, and both keep working. It is a general policy, not a per-appliance choice, and the reason for keeping the old name is the same one that keeps `/etc/turnkey_version` in 0014 — compatibility with TurnKey appliances is a stated property of this project (0008). ## Responding to the review **HIGH — the inventory contradicted the note's own scope rule.** Correct, and it did so in the direction that makes a contributor under-apply the policy. The rule covers "a script installed by a package this project builds"; the table listed only the two keel-wordpress overlay files, the two in `common`, and the foreign packages going to tracker#13. So the note handled somebody else's packages and silently dropped the ones we build. I verified the five myself against the dpkg file lists and against the forks' own trees, and added them: | Command | Package | Source | | --- | --- | --- | | `/usr/sbin/turnkey-init` | `inithooks 2.3.6+keel4` | `Keel-Linux/inithooks`, `turnkey-init` | | `/usr/sbin/turnkey-sudoadmin` | `inithooks 2.3.6+keel4` | `Keel-Linux/inithooks`, `turnkey-sudoadmin` | | `/usr/sbin/turnkey-install-security-updates` | `inithooks 2.3.6+keel4` | `Keel-Linux/inithooks`, `turnkey-install-security-updates` | | `/usr/lib/inithooks/bin/turnkey-init-fence` | `inithooks 2.3.6+keel4` | `Keel-Linux/inithooks`, `bin/turnkey-init-fence` | | `/usr/bin/turnkey-lexicon` | `confconsole 2.2.3+keel2` | `Keel-Linux/confconsole`, `turnkey-lexicon` | Also added, from LOW 1: `turnkey-artisan` and `turnkey-composer`, tracked `100755` files in `common` reached through `laravel.mk` and `composer.mk`; and the byte-identical second copy of `turnkey-mysql-install-perf-info-schemas` in `unit-mariadb` (same blob `c8f185b2`), which the old table attributed to `common` alone, so anybody following it would have renamed one of two copies. `turnkey-make-ssl-cert` from `turnkey-ssl 3.1.1` is now listed too, explicitly out of scope, so the next reader does not have to derive that again. The section is restructured: **fifteen** distinct names grouped by what puts the file on the machine — appliance overlay, `common` overlay, a package we build, a package somebody else builds — because that is what decides when each one can move. **Eleven are ours.** `turnkey-init-fence` is marked as not on `PATH` and not a command an operator types, with the observation that the same name is also a conffile, a systemd unit and two firstboot hooks, which is a packaging question the note does not settle. And the section now closes by saying that when the table and the rule disagree, **the rule wins** — a table is a measurement of one day and this one has been corrected three times. I took the reviewer's framing on the `+keel` suffix: the rule reads on "a package this project builds", and `turnkey-sysinfo` 1.1.0, `turnkey-version` 1.1.3 and `turnkey-ssl` 3.1.1 carry no such suffix while `inithooks` and `confconsole` do. The "what a package owns does instead" section now opens by saying it is about packages we do **not** build, since getting that the wrong way round is precisely the mistake the first version made. **MEDIUM — the `-d` claim.** Correct, and worse in a note than in code, because a note is applied without asking. Measured with coreutils 9.7 and written into the note as a table: ``` cp -TR → link -> real symlink kept, hard link split (nlink 1) cp -TdR → link -> real symlink kept, hard link kept (nlink 2) cp -TLR → link a second regular file — this one flattens ``` The property is the absence of `-L`, not the presence of `-d`. keel-wordpress#6 now has a test asserting all three rows, so the claim is checked rather than written down. **LOW 2** — the Decision section now states that the link is relative and why an absolute one breaks the build-time caller, and that the Keel name is the `turnkey-` prefix replaced by `keel-` and nothing else. **LOW 3** — "must be covered", and it names who is owed those tests. **LOW 4** — the reverse-dependency provenance says the container throughout. **LOW 5** — merge order is stated at the top of both pull request descriptions. ## Test plan - [x] The number is 0015 and only 0015; 0014 is being written concurrently and is untouched here. - [x] The note follows the form of 0001-0013: title line, `Date`, `Status`, `## Decision`, the three-part justification of brief section 10, `## Consequences`. - [x] Every added row verified against `dpkg -L`/`dpkg -S` file lists, `git ls-files` across the organization's repositories, and the `.mk` files that pull each overlay. - [ ] Maintainer confirms the note matches the decision taken on 2026-09-28, and in particular the reviewer's reading that "a package this project builds" means a package carrying a `+keel` version. This repository ships no package and has no `package / changelog` gate, so there is no changelog entry to add, and no checks are reported on the branch.
marcos-mendez commented 2026-09-28 01:23:02 -03:00 (Migrated from github.com)

Review

I checked the note's factual claims against fab's source, this organization's repositories and the booted wordpress-demo container rather than taking them as read. The reverse-dependency argument, the turnkey-detect-virt correction and the common-ownership correction all hold. One finding is HIGH: the note's inventory contradicts the note's own scope rule, and it does so in the direction that makes a future contributor under-apply the policy.

Verified

  • Lines 46-51, the closed set: correct. On the container, apt-cache rdepends turnkey-sysinfo is empty both installed-only and archive-wide, and turnkey-version's only reverse dependency is turnkey-sysinfo (turnkey-sysinfo: Depends: python3:any, turnkey-netinfo, turnkey-version). Versions are 1.1.0 and 1.1.3 as stated.
  • Lines 53-60, six not five: correct. dpkg -L turnkey-sysinfo lists /usr/bin/turnkey-sysinfo, /usr/bin/turnkey-detect-virt, libsysinfo/{__init__,disk,memstats,virt}.py and /usr/share/turnkey-sysinfo/contrib/motd. The library and the motd fragment are real and tracker#13 does need both names.
  • Lines 78-89, the common correction: correct. overlays/php/usr/local/bin/turnkey-php and overlays/mysql/usr/local/bin/turnkey-mysql-install-perf-info-schemas are ordinary tracked 100755 files in Keel-Linux/common, reaching the image through COMMON_OVERLAYS. On the appliance both are NOT owned by any package, which settles "inherited" against tracker#12. The single caller inside common is real: overlays/mysql/usr/lib/confconsole/plugins.d/System_Settings/Mysql_perf_info.py:19-20 calls it by bare name.
  • Lines 34-37, the dpkg reasoning: correct as stated.
  • Lines 102-114: the keel-wordpress tests do what the note says they do. I ran them.

HIGH — the table omits five commands that the note's own scope rule pulls in, and they ship on every appliance

Lines 15-20 against the table at 66-72.

The rule is: the policy applies to "every operator facing command a repository of this organization writes and names itself: a file in an appliance overlay, a file in the common overlays, a script installed by a package this project builds". The carve-out is for "a command that only exists because some other project's package put it there".

Measured on the booted wordpress-demo container, the third clause covers five commands the table never mentions:

Command Package Version installed
/usr/sbin/turnkey-init inithooks 2.3.6+keel4
/usr/sbin/turnkey-sudoadmin inithooks 2.3.6+keel4
/usr/sbin/turnkey-install-security-updates inithooks 2.3.6+keel4
/usr/lib/inithooks/bin/turnkey-init-fence inithooks 2.3.6+keel4
/usr/bin/turnkey-lexicon confconsole 2.2.3+keel2

Both packages carry a +keel suffix — they are built from Keel-Linux/inithooks and Keel-Linux/confconsole and keel-wordpress's own Makefile:40-41 names them among "the project's own packages". turnkey-init is the most operator-facing command on the appliance; it is what an operator types to re-run first-boot configuration.

Meanwhile the row the table does devote to packages sends turnkey-sysinfo, turnkey-version and turnkey-detect-virt to tracker#13 — and those come from turnkey-sysinfo 1.1.0 and turnkey-version 1.1.3, neither carrying a +keel suffix, i.e. the packages that are genuinely somebody else's. So the note handles the foreign packages and silently drops the ones this project already builds, which is the opposite of what the rule at 15-20 says. (A sixth, /usr/bin/turnkey-make-ssl-cert from turnkey-ssl 3.1.1, is also unmentioned; that one is correctly out of scope under the carve-out, but the note never says so either.)

The scenario this bites in is the one the note is written to prevent. A contributor picks up tracker#12, reads 0015 as the settled scope, sees turnkey-init is not in the table and concludes it is inherited surface. Nothing corrects them: there is no gate for a name, the note says at 153-156 that only grep -rn finds these, and the table has already been through two rounds of correction, which makes it read as authoritative.

The fix is small — either add the rows, or add one sentence saying the table covers the commands reachable from tracker#12's inventory and that packaged commands from inithooks and confconsole are unenumerated and still in scope.

MEDIUM — the -d claim is the wrong half of the flag, in the document a contributor will copy from

Lines 95-97: "-d is --no-dereference --preserve=links, so a symlink in an overlay arrives in the image as a symlink."

The conclusion is right and I verified it end to end. The attribution is not. In GNU cp, -P/--no-dereference is already the default under -R; only -L dereferences. Measured:

cp -TR  →  link -> real     (symlink preserved; hard link split, nlink 1)
cp -TdR →  link -> real     (symlink preserved; hard link kept, nlink 2)
cp -TLR →  link             (regular file — this is the one that flattens)

What -d contributes to fab-apply-overlay is --preserve=links, i.e. hard links. Symlink survival hinges on nobody passing -L.

This matters more here than in the code, because a decision note is applied without asking. As written it teaches that any copy step lacking -d will flatten links, which will get -d added where it does nothing, or a perfectly sound mechanism rejected for not having it. And the verification claimed at 102-109 cannot catch the difference: I dropped -d from tests/wrappers.bats's own cp and no test failed.

Suggested: "-R copies a symlink as a symlink unless -L is given, and -d additionally preserves hard links, so a symlink in an overlay arrives in the image as a symlink."


LOW 1 — two more common overlay commands, and a duplicate, unlisted

common/overlays/artisan/usr/local/bin/turnkey-artisan and common/overlays/composer/usr/local/bin/turnkey-composer are tracked 100755 files in common, reached through laravel.mk:3 and composer.mk:1. turnkey-artisan is the same runuser-based shape as turnkey-wp. Neither word appears anywhere in the note's 165 lines. They ship on no Keel appliance today, because nothing includes those mk files — which is exactly why they should be named rather than left to be rediscovered when something does.

Separately, unit-mariadb/overlay/usr/local/bin/turnkey-mysql-install-perf-info-schemas is a byte-identical second copy (same blob) of the common one. The table attributes that command to common alone, so a contributor following the note renames one of two copies and leaves the appliances built from the unit on the old name.

The three bullets at 11-13 are otherwise applicable without asking. They do not say whether turnkey-x points at keel-x or at /usr/local/bin/keel-x. keel-wordpress chose relative and asserts it (readlink = keel-wp), which is the right answer: an absolute link does not resolve inside fab-chroot, so a build-time caller like conf.d/main breaks. I confirmed the failure mode — making the link absolute breaks three of that repository's tests. Absolute is the more natural thing to type, so one clause in the Decision section would save the next person the build.

Related and smaller: the note never states how the Keel name is derived. The table implies a mechanical turnkey- → keel- substitution; saying so removes the only other open question.

LOW 3 — a requirement written in the present tense reads as a description

Lines 157-159: "The compatibility names are covered by tests that run them, in every repository that has a pair." Today that is true of keel-wordpress and of nothing else — common has four such commands and no such test. As a Consequences bullet it is a rule; "must be covered" makes it one.

LOW 4 — where a measurement came from

Line 47 says the reverse-dependency figures were "measured on the wordpress-demo container", line 54 says the dpkg file list is "on the build host". Both numbers are right; I reproduced them on the container. The note is careful about provenance everywhere else, so the mismatch is worth a word.

LOW 5 — ordering against keel-wordpress#6

Keel-Linux/keel-wordpress#6 cites "decision 0015 of the handbook" in changelog, COVERAGE.md, tests/wrappers.bats and in both scripts' header comments. 0015 is not on main here yet. If #6 merges first the appliance ships comments pointing at a note that does not exist. Not a defect in either branch, just a merge order worth stating.


What I verified versus what I inferred

Verified: every dpkg fact above, by dpkg -L, dpkg -S, dpkg-query -W and apt-cache rdepends on the booted wordpress-demo; the common and unit-mariadb file inventory by git ls-files across all 21 repositories; the Mysql_perf_info.py caller; cp's behaviour under -R, -dR and -LR; and that dropping -d from the keel-wordpress test changes no test result.

Inferred: that the five inithooks/confconsole commands are in scope. That is my reading of lines 16-18 applied to packages carrying a +keel version, and it is the reading that makes the rule and the carve-out consistent. If the maintainer intends "a package this project builds" to mean only packages whose name is Keel's, the note should say that, because on the present wording those five are in.

Warning. The HIGH finding is not a reason to hold the decision — the policy itself is sound, well argued, and the three-part justification does the work section 10 asks of it. But the table will be read as the scope, and as it stands it excludes commands the note's own rule includes. I would add the rows or the disclaiming sentence before merge, and take the -d correction at the same time since it is one clause.

## Review I checked the note's factual claims against fab's source, this organization's repositories and the booted `wordpress-demo` container rather than taking them as read. The reverse-dependency argument, the `turnkey-detect-virt` correction and the `common`-ownership correction all hold. One finding is HIGH: the note's inventory contradicts the note's own scope rule, and it does so in the direction that makes a future contributor under-apply the policy. ### Verified - Lines 46-51, the closed set: correct. On the container, `apt-cache rdepends turnkey-sysinfo` is empty both installed-only and archive-wide, and `turnkey-version`'s only reverse dependency is `turnkey-sysinfo` (`turnkey-sysinfo: Depends: python3:any, turnkey-netinfo, turnkey-version`). Versions are 1.1.0 and 1.1.3 as stated. - Lines 53-60, six not five: correct. `dpkg -L turnkey-sysinfo` lists `/usr/bin/turnkey-sysinfo`, `/usr/bin/turnkey-detect-virt`, `libsysinfo/{__init__,disk,memstats,virt}.py` and `/usr/share/turnkey-sysinfo/contrib/motd`. The library and the motd fragment are real and tracker#13 does need both names. - Lines 78-89, the `common` correction: correct. `overlays/php/usr/local/bin/turnkey-php` and `overlays/mysql/usr/local/bin/turnkey-mysql-install-perf-info-schemas` are ordinary tracked `100755` files in `Keel-Linux/common`, reaching the image through `COMMON_OVERLAYS`. On the appliance both are `NOT owned by any package`, which settles "inherited" against tracker#12. The single caller inside `common` is real: `overlays/mysql/usr/lib/confconsole/plugins.d/System_Settings/Mysql_perf_info.py:19-20` calls it by bare name. - Lines 34-37, the dpkg reasoning: correct as stated. - Lines 102-114: the keel-wordpress tests do what the note says they do. I ran them. --- ### HIGH — the table omits five commands that the note's own scope rule pulls in, and they ship on every appliance Lines 15-20 against the table at 66-72. The rule is: the policy applies to "every operator facing command a repository of this organization **writes and names itself**: a file in an appliance overlay, a file in the `common` overlays, **a script installed by a package this project builds**". The carve-out is for "a command that only exists because some other project's package put it there". Measured on the booted `wordpress-demo` container, the third clause covers five commands the table never mentions: | Command | Package | Version installed | | --- | --- | --- | | `/usr/sbin/turnkey-init` | `inithooks` | `2.3.6+keel4` | | `/usr/sbin/turnkey-sudoadmin` | `inithooks` | `2.3.6+keel4` | | `/usr/sbin/turnkey-install-security-updates` | `inithooks` | `2.3.6+keel4` | | `/usr/lib/inithooks/bin/turnkey-init-fence` | `inithooks` | `2.3.6+keel4` | | `/usr/bin/turnkey-lexicon` | `confconsole` | `2.2.3+keel2` | Both packages carry a `+keel` suffix — they are built from `Keel-Linux/inithooks` and `Keel-Linux/confconsole` and `keel-wordpress`'s own `Makefile:40-41` names them among "the project's own packages". `turnkey-init` is the most operator-facing command on the appliance; it is what an operator types to re-run first-boot configuration. Meanwhile the row the table *does* devote to packages sends `turnkey-sysinfo`, `turnkey-version` and `turnkey-detect-virt` to tracker#13 — and those come from `turnkey-sysinfo` 1.1.0 and `turnkey-version` 1.1.3, neither carrying a `+keel` suffix, i.e. the packages that are genuinely somebody else's. So the note handles the foreign packages and silently drops the ones this project already builds, which is the opposite of what the rule at 15-20 says. (A sixth, `/usr/bin/turnkey-make-ssl-cert` from `turnkey-ssl` 3.1.1, is also unmentioned; that one is correctly out of scope under the carve-out, but the note never says so either.) The scenario this bites in is the one the note is written to prevent. A contributor picks up tracker#12, reads 0015 as the settled scope, sees `turnkey-init` is not in the table and concludes it is inherited surface. Nothing corrects them: there is no gate for a name, the note says at 153-156 that only `grep -rn` finds these, and the table has already been through two rounds of correction, which makes it read as authoritative. The fix is small — either add the rows, or add one sentence saying the table covers the commands reachable from tracker#12's inventory and that packaged commands from `inithooks` and `confconsole` are unenumerated and still in scope. ### MEDIUM — the `-d` claim is the wrong half of the flag, in the document a contributor will copy from Lines 95-97: "`-d` is `--no-dereference --preserve=links`, so a symlink in an overlay arrives in the image as a symlink." The conclusion is right and I verified it end to end. The attribution is not. In GNU cp, `-P/--no-dereference` is already the default under `-R`; only `-L` dereferences. Measured: ``` cp -TR → link -> real (symlink preserved; hard link split, nlink 1) cp -TdR → link -> real (symlink preserved; hard link kept, nlink 2) cp -TLR → link (regular file — this is the one that flattens) ``` What `-d` contributes to `fab-apply-overlay` is `--preserve=links`, i.e. hard links. Symlink survival hinges on nobody passing `-L`. This matters more here than in the code, because a decision note is applied without asking. As written it teaches that any copy step lacking `-d` will flatten links, which will get `-d` added where it does nothing, or a perfectly sound mechanism rejected for not having it. And the verification claimed at 102-109 cannot catch the difference: I dropped `-d` from `tests/wrappers.bats`'s own `cp` and no test failed. Suggested: "`-R` copies a symlink as a symlink unless `-L` is given, and `-d` additionally preserves hard links, so a symlink in an overlay arrives in the image as a symlink." --- ### LOW 1 — two more `common` overlay commands, and a duplicate, unlisted `common/overlays/artisan/usr/local/bin/turnkey-artisan` and `common/overlays/composer/usr/local/bin/turnkey-composer` are tracked `100755` files in `common`, reached through `laravel.mk:3` and `composer.mk:1`. `turnkey-artisan` is the same `runuser`-based shape as `turnkey-wp`. Neither word appears anywhere in the note's 165 lines. They ship on no Keel appliance today, because nothing includes those mk files — which is exactly why they should be named rather than left to be rediscovered when something does. Separately, `unit-mariadb/overlay/usr/local/bin/turnkey-mysql-install-perf-info-schemas` is a byte-identical second copy (same blob) of the `common` one. The table attributes that command to `common` alone, so a contributor following the note renames one of two copies and leaves the appliances built from the unit on the old name. ### LOW 2 — the policy does not say the link must be relative, and the natural choice is wrong The three bullets at 11-13 are otherwise applicable without asking. They do not say whether `turnkey-x` points at `keel-x` or at `/usr/local/bin/keel-x`. keel-wordpress chose relative and asserts it (`readlink` = `keel-wp`), which is the right answer: an absolute link does not resolve inside `fab-chroot`, so a build-time caller like `conf.d/main` breaks. I confirmed the failure mode — making the link absolute breaks three of that repository's tests. Absolute is the more natural thing to type, so one clause in the Decision section would save the next person the build. Related and smaller: the note never states how the Keel name is derived. The table implies a mechanical `turnkey-` → `keel-` substitution; saying so removes the only other open question. ### LOW 3 — a requirement written in the present tense reads as a description Lines 157-159: "The compatibility names **are covered** by tests that run them, in every repository that has a pair." Today that is true of keel-wordpress and of nothing else — `common` has four such commands and no such test. As a Consequences bullet it is a rule; "must be covered" makes it one. ### LOW 4 — where a measurement came from Line 47 says the reverse-dependency figures were "measured on the `wordpress-demo` container", line 54 says the dpkg file list is "on the build host". Both numbers are right; I reproduced them on the container. The note is careful about provenance everywhere else, so the mismatch is worth a word. ### LOW 5 — ordering against keel-wordpress#6 `Keel-Linux/keel-wordpress#6` cites "decision 0015 of the handbook" in `changelog`, `COVERAGE.md`, `tests/wrappers.bats` and in both scripts' header comments. 0015 is not on `main` here yet. If #6 merges first the appliance ships comments pointing at a note that does not exist. Not a defect in either branch, just a merge order worth stating. --- ### What I verified versus what I inferred Verified: every dpkg fact above, by `dpkg -L`, `dpkg -S`, `dpkg-query -W` and `apt-cache rdepends` on the booted `wordpress-demo`; the `common` and `unit-mariadb` file inventory by `git ls-files` across all 21 repositories; the `Mysql_perf_info.py` caller; cp's behaviour under `-R`, `-dR` and `-LR`; and that dropping `-d` from the keel-wordpress test changes no test result. Inferred: that the five `inithooks`/`confconsole` commands are in scope. That is my reading of lines 16-18 applied to packages carrying a `+keel` version, and it is the reading that makes the rule and the carve-out consistent. If the maintainer intends "a package this project builds" to mean only packages whose *name* is Keel's, the note should say that, because on the present wording those five are in. **Warning.** The HIGH finding is not a reason to hold the decision — the policy itself is sound, well argued, and the three-part justification does the work section 10 asks of it. But the table will be read as the scope, and as it stands it excludes commands the note's own rule includes. I would add the rows or the disclaiming sentence before merge, and take the `-d` correction at the same time since it is one clause.
marcos-mendez commented 2026-09-28 02:01:29 -03:00 (Migrated from github.com)

Re-review

I re-measured the inventory rather than reading the new tables, and checked the two claims that carry weight.

The HIGH is resolved

The five commands from packages this project builds are in, as their own group, with the +keel versions that make them ours rather than inherited. The section at 148-156 states the reasoning that was missing, and 40-46 fixes the thing that actually caused the error: the previous heading said "owned by a Debian package", which conflated "packaged" with "somebody else's", and it now says "owned by a package this project does not build" with the +keel test spelled out. That is the correction at the level of the mistake, not at the level of the symptom.

Re-measured independently and the arithmetic holds. Fifteen distinct names, eleven ours: turnkey-wp, turnkey-wordpress-update, turnkey-mysql-install-perf-info-schemas, turnkey-php, turnkey-artisan, turnkey-composer, turnkey-init, turnkey-sudoadmin, turnkey-install-security-updates, turnkey-init-fence, turnkey-lexicon; four not ours: turnkey-sysinfo, turnkey-detect-virt, turnkey-version, turnkey-make-ssl-cert. Justification bullet 1's "thirteen of the fifteen present on a wordpress-demo container, nine of those ours" also matches what the container reports — turnkey-artisan and turnkey-composer are the two absent, correctly, since nothing includes laravel.mk or composer.mk.

The blob claim is exact. git rev-parse gives c8f185b2ab5cdf273d1b9c11b0cf0e2ab073a792 for both common/overlays/mysql/usr/local/bin/turnkey-mysql-install-perf-info-schemas and unit-mariadb/overlay/usr/local/bin/turnkey-mysql-install-perf-info-schemas, and the working files share a sha256. The consequence the note draws at 99-105 is the right one and is the reason that row earns a place in a table it otherwise duplicates.

The turnkey-init-fence paragraph at 158-164 is precise too. git ls-files in inithooks gives exactly what it names: bin/turnkey-init-fence, debian/inithooks.turnkey-init-fence.service, default/turnkey-init-fence, firstboot.d/30turnkey-init-fence, firstboot.d/97turnkey-init-fence-disable and turnkey-init-fence/htdocs/. Every item checks out, and drawing the line at "the compatibility rule covers the command, the unit and conffile names are a packaging question" is the right place to stop a decision note.

turnkey-make-ssl-cert as explicitly out of scope, with turnkey-ssl 3.1.1 carrying no +keel, is correct and worth having written down — an unlisted name and a name listed as out of scope read very differently to the next person.

"When the table and the rule disagree, the rule wins" — genuine, on this evidence

I was ready to read that as a way to keep shipping a wrong table, and on its own it would be. It is not on its own. It arrives with a table that went from five names to fifteen, a stated grouping principle (by what puts the file on the machine, because that is what decides when each can move), and an instruction rather than an excuse: "the right fix is to add the row". A precedence clause that licenses inaction looks different — it appears instead of a measurement, not after a better one.

The clause is also just true. A table is a measurement of one day and this one will go stale the next time a repository gains a command; a policy whose scope is defined by an enumeration is a policy that silently shrinks. Putting the rule above the table is what stops that.

What would make it self-correcting rather than merely honest is the one thing still missing, and it is a LOW suggestion, not a condition. Nothing re-measures. The Consequences bullet already says finding these is grep -rn and not a green build, which is exactly the observation that a five-line check over the organization's trees — every turnkey-* executable, diffed against the names in this note — would turn into something that fails when the table drifts. The note argues for that check without proposing it.

The -d correction

Taken, and stated correctly at 191-210: -P is already the default under -R, -d adds --preserve=links for hard links, and the property the policy depends on is the absence of -L. The measured table with coreutils 9.7 matches what I measured. The paragraph at 198-210 explains why the attribution matters, which is the part that keeps the next person from adding -d where it does nothing.

More to the point, it is no longer only prose. keel-wordpress now has a test that pins it, and I confirmed that test fails when the claim is violated: with a cp shim that dereferences under -R, three tests go red including the one that names this property. The sentence at 219-222 is therefore accurate.

The other items from the first pass

All taken. The relative-link rule and the name-derivation rule are now in the Decision section at 15-22, where a contributor applying the policy will meet them, with the fab-chroot reason attached. "The compatibility names must be covered" replaces the present-tense description, and 279-281 names who owes them. The provenance mismatch is gone — line 70 now says wordpress-demo container rather than build host, which matches where the measurement came from. The closing correction about turnkey-detect-virt no longer frames itself as "five are really six", which was the sentence that made the old table sound settled.

Verified versus inferred

Verified: the blob identity by git rev-parse in both repositories and by sha256; the fifteen/eleven and thirteen/nine counts by re-listing from the repositories and from the container; the turnkey-init-fence file set by git ls-files; the +keel suffixes by dpkg-query -W on the container; the cp semantics by measurement; and that the keel-wordpress test backing 219-222 fails when the property is false.

Inferred: that the precedence clause is a resolution rather than an escape. That is a judgement about intent, and the evidence for it is that the measurement tripled in the same commit.

Approve. Clean above LOW. The single LOW — nothing re-measures the inventory — is a suggestion for a follow-up, not a condition on this note.

## Re-review I re-measured the inventory rather than reading the new tables, and checked the two claims that carry weight. ### The HIGH is resolved The five commands from packages this project builds are in, as their own group, with the `+keel` versions that make them ours rather than inherited. The section at 148-156 states the reasoning that was missing, and 40-46 fixes the thing that actually caused the error: the previous heading said "owned by a Debian package", which conflated "packaged" with "somebody else's", and it now says "owned by a package **this project does not build**" with the `+keel` test spelled out. That is the correction at the level of the mistake, not at the level of the symptom. Re-measured independently and the arithmetic holds. Fifteen distinct names, eleven ours: `turnkey-wp`, `turnkey-wordpress-update`, `turnkey-mysql-install-perf-info-schemas`, `turnkey-php`, `turnkey-artisan`, `turnkey-composer`, `turnkey-init`, `turnkey-sudoadmin`, `turnkey-install-security-updates`, `turnkey-init-fence`, `turnkey-lexicon`; four not ours: `turnkey-sysinfo`, `turnkey-detect-virt`, `turnkey-version`, `turnkey-make-ssl-cert`. Justification bullet 1's "thirteen of the fifteen present on a `wordpress-demo` container, nine of those ours" also matches what the container reports — `turnkey-artisan` and `turnkey-composer` are the two absent, correctly, since nothing includes `laravel.mk` or `composer.mk`. **The blob claim is exact.** `git rev-parse` gives `c8f185b2ab5cdf273d1b9c11b0cf0e2ab073a792` for both `common/overlays/mysql/usr/local/bin/turnkey-mysql-install-perf-info-schemas` and `unit-mariadb/overlay/usr/local/bin/turnkey-mysql-install-perf-info-schemas`, and the working files share a sha256. The consequence the note draws at 99-105 is the right one and is the reason that row earns a place in a table it otherwise duplicates. The `turnkey-init-fence` paragraph at 158-164 is precise too. `git ls-files` in `inithooks` gives exactly what it names: `bin/turnkey-init-fence`, `debian/inithooks.turnkey-init-fence.service`, `default/turnkey-init-fence`, `firstboot.d/30turnkey-init-fence`, `firstboot.d/97turnkey-init-fence-disable` and `turnkey-init-fence/htdocs/`. Every item checks out, and drawing the line at "the compatibility rule covers the command, the unit and conffile names are a packaging question" is the right place to stop a decision note. `turnkey-make-ssl-cert` as explicitly out of scope, with `turnkey-ssl` 3.1.1 carrying no `+keel`, is correct and worth having written down — an unlisted name and a name listed as out of scope read very differently to the next person. ### "When the table and the rule disagree, the rule wins" — genuine, on this evidence I was ready to read that as a way to keep shipping a wrong table, and on its own it would be. It is not on its own. It arrives with a table that went from five names to fifteen, a stated grouping principle (by what puts the file on the machine, because that is what decides when each can move), and an instruction rather than an excuse: "the right fix is to add the row". A precedence clause that licenses inaction looks different — it appears instead of a measurement, not after a better one. The clause is also just true. A table is a measurement of one day and this one will go stale the next time a repository gains a command; a policy whose scope is defined by an enumeration is a policy that silently shrinks. Putting the rule above the table is what stops that. What would make it self-correcting rather than merely honest is the one thing still missing, and it is a LOW suggestion, not a condition. Nothing re-measures. The Consequences bullet already says finding these is `grep -rn` and not a green build, which is exactly the observation that a five-line check over the organization's trees — every `turnkey-*` executable, diffed against the names in this note — would turn into something that fails when the table drifts. The note argues for that check without proposing it. ### The `-d` correction Taken, and stated correctly at 191-210: `-P` is already the default under `-R`, `-d` adds `--preserve=links` for hard links, and the property the policy depends on is the absence of `-L`. The measured table with coreutils 9.7 matches what I measured. The paragraph at 198-210 explains why the attribution matters, which is the part that keeps the next person from adding `-d` where it does nothing. More to the point, it is no longer only prose. keel-wordpress now has a test that pins it, and I confirmed that test fails when the claim is violated: with a `cp` shim that dereferences under `-R`, three tests go red including the one that names this property. The sentence at 219-222 is therefore accurate. ### The other items from the first pass All taken. The relative-link rule and the name-derivation rule are now in the Decision section at 15-22, where a contributor applying the policy will meet them, with the `fab-chroot` reason attached. "The compatibility names **must be covered**" replaces the present-tense description, and 279-281 names who owes them. The provenance mismatch is gone — line 70 now says `wordpress-demo` container rather than build host, which matches where the measurement came from. The closing correction about `turnkey-detect-virt` no longer frames itself as "five are really six", which was the sentence that made the old table sound settled. ### Verified versus inferred Verified: the blob identity by `git rev-parse` in both repositories and by sha256; the fifteen/eleven and thirteen/nine counts by re-listing from the repositories and from the container; the `turnkey-init-fence` file set by `git ls-files`; the `+keel` suffixes by `dpkg-query -W` on the container; the cp semantics by measurement; and that the keel-wordpress test backing 219-222 fails when the property is false. Inferred: that the precedence clause is a resolution rather than an escape. That is a judgement about intent, and the evidence for it is that the measurement tripled in the same commit. **Approve.** Clean above LOW. The single LOW — nothing re-measures the inventory — is a suggestion for a follow-up, not a condition on this note.
Sign in to join this conversation.
No description provided.