The adr skill's template shows the frontmatter number unquoted:
adr:0004
Under YAML 1.1 — which PyYAML's safe_load implements — a leading-zero integer is parsed as octal. So:
0001–0007 parse as ints 1–7
0008 and 0009 parse as the strings "0008" and "0009", because 8 and 9 are not octal digits
the same happens at 0018/0019, 0028/0029, and so on
The block still loads, so nothing fails loudly. The corpus simply acquires a silent type split, and a query like [a for a in adrs if a['adr'] == 8] misses the ADR it was looking for. That is the failure mode the skill's own "The frontmatter must actually parse" section is written against, one level subtler — the two documented traps (supersedes: -, unquoted deciders: pending: X) break loading, this one corrupts typing.
Found where
Writing cobalt-dingo ADR-0007 and ADR-0008 on 2026-09-16. Validated with the skill's own check command, which prints ok for both and hides the discrepancy because it does not show the parsed type:
Fixed in that repo by quoting the value in all eight files (adr: "0008"), which makes every entry a consistently-typed string and keeps the four-digit convention intact.
Estate-wide exposure
Every repo keeping an independently-numbered series is on the same trajectory: infra, cad, swedsl, brain-gardener, openbanking-api, agentsquad, cobalt-dingo. Any series that has reached 8 already has the split; infra is well past 0019 by the skill's own examples, so it likely has several.
Proposed fix
Change the template in skills/adr/SKILL.md to adr: "0004" and add the trap to the "must actually parse" table, alongside the bare-dash and nested-colon entries.
Strengthen the check command so it reveals typing rather than just success:
python3 -c "import yaml,sys;d=yaml.safe_load(open(sys.argv[1]).read().split('---')[1]);assert isinstance(d['adr'],str),f\"adr must be a quoted string, got {type(d['adr']).__name__}\";print('ok',d['adr'])" docs/adr/0004-*.md
Optionally sweep the existing series. Mechanical: sed -i -E 's/^(adr: +)([0-9]{4})$/\1"\2"/' docs/adr/0*.md. This is frontmatter quoting, not a content change, so it does not violate the skill's own "never edit an accepted ADR" rule — but worth noting in the sweep commit that it is the amendment class.
Low risk, and cheaper now than after another two series cross 0008.
## The bug
The `adr` skill's template shows the frontmatter number unquoted:
```yaml
adr: 0004
```
Under YAML 1.1 — which PyYAML's `safe_load` implements — a leading-zero integer is parsed as **octal**. So:
- `0001`–`0007` parse as ints `1`–`7`
- **`0008` and `0009` parse as the strings `"0008"` and `"0009"`**, because 8 and 9 are not octal digits
- the same happens at `0018`/`0019`, `0028`/`0029`, and so on
The block still loads, so nothing fails loudly. The corpus simply acquires a silent type split, and a query like `[a for a in adrs if a['adr'] == 8]` misses the ADR it was looking for. That is the failure mode the skill's own "The frontmatter must actually parse" section is written against, one level subtler — the two documented traps (`supersedes: -`, unquoted `deciders: pending: X`) break loading, this one corrupts typing.
## Found where
Writing `cobalt-dingo` ADR-0007 and ADR-0008 on 2026-09-16. Validated with the skill's own check command, which prints `ok` for both and hides the discrepancy because it does not show the parsed type:
```console
$ python3 -c "import yaml,sys;d=yaml.safe_load(open(sys.argv[1]).read().split('---')[1]);print(repr(d['adr']))" docs/adr/0007-*.md
7
$ ... docs/adr/0008-*.md
'0008'
```
Fixed in that repo by quoting the value in all eight files (`adr: "0008"`), which makes every entry a consistently-typed string and keeps the four-digit convention intact.
## Estate-wide exposure
Every repo keeping an independently-numbered series is on the same trajectory: `infra`, `cad`, `swedsl`, `brain-gardener`, `openbanking-api`, `agentsquad`, `cobalt-dingo`. Any series that has reached 8 already has the split; `infra` is well past 0019 by the skill's own examples, so it likely has several.
## Proposed fix
1. Change the template in `skills/adr/SKILL.md` to `adr: "0004"` and add the trap to the "must actually parse" table, alongside the bare-dash and nested-colon entries.
2. Strengthen the check command so it reveals typing rather than just success:
```sh
python3 -c "import yaml,sys;d=yaml.safe_load(open(sys.argv[1]).read().split('---')[1]);assert isinstance(d['adr'],str),f\"adr must be a quoted string, got {type(d['adr']).__name__}\";print('ok',d['adr'])" docs/adr/0004-*.md
```
3. Optionally sweep the existing series. Mechanical: `sed -i -E 's/^(adr: +)([0-9]{4})$/\1"\2"/' docs/adr/0*.md`. This is frontmatter quoting, not a content change, so it does not violate the skill's own "never edit an accepted ADR" rule — but worth noting in the sweep commit that it is the amendment class.
Low risk, and cheaper now than after another two series cross 0008.
---
_Created via git-mcp on behalf of @mathias_
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
The bug
The
adrskill's template shows the frontmatter number unquoted:Under YAML 1.1 — which PyYAML's
safe_loadimplements — a leading-zero integer is parsed as octal. So:0001–0007parse as ints1–70008and0009parse as the strings"0008"and"0009", because 8 and 9 are not octal digits0018/0019,0028/0029, and so onThe block still loads, so nothing fails loudly. The corpus simply acquires a silent type split, and a query like
[a for a in adrs if a['adr'] == 8]misses the ADR it was looking for. That is the failure mode the skill's own "The frontmatter must actually parse" section is written against, one level subtler — the two documented traps (supersedes: -, unquoteddeciders: pending: X) break loading, this one corrupts typing.Found where
Writing
cobalt-dingoADR-0007 and ADR-0008 on 2026-09-16. Validated with the skill's own check command, which printsokfor both and hides the discrepancy because it does not show the parsed type:Fixed in that repo by quoting the value in all eight files (
adr: "0008"), which makes every entry a consistently-typed string and keeps the four-digit convention intact.Estate-wide exposure
Every repo keeping an independently-numbered series is on the same trajectory:
infra,cad,swedsl,brain-gardener,openbanking-api,agentsquad,cobalt-dingo. Any series that has reached 8 already has the split;infrais well past 0019 by the skill's own examples, so it likely has several.Proposed fix
Change the template in
skills/adr/SKILL.mdtoadr: "0004"and add the trap to the "must actually parse" table, alongside the bare-dash and nested-colon entries.Strengthen the check command so it reveals typing rather than just success:
Optionally sweep the existing series. Mechanical:
sed -i -E 's/^(adr: +)([0-9]{4})$/\1"\2"/' docs/adr/0*.md. This is frontmatter quoting, not a content change, so it does not violate the skill's own "never edit an accepted ADR" rule — but worth noting in the sweep commit that it is the amendment class.Low risk, and cheaper now than after another two series cross 0008.
Created via git-mcp on behalf of @mathias