From 0676108ec419a87a40479b63deb40fd4b68d7bdf Mon Sep 17 00:00:00 2001 From: REPPL <77722411+REPPL@users.noreply.github.com> Date: Thu, 17 Sep 2026 07:37:50 +0100 Subject: [PATCH 1/2] chore: file twelve issues captured in the bugfix worktree on 12 and 15 September Findings from the v0.7.1-to-v0.8.0 install and capture sessions: ahoy install leaves git config alone after writing the hook, a foreign regular file at the PATH entry is misreported, the PATH entry written as a symlink to the plugin-root binary, the scan-deep prompt's placement, the three required config gaps, `--json` emitting `gaps: null`, staleness reading unknown on a pinned install, the closed sets `--source` and `--category` name in help, the upgrade path self-blocking across a plugin update, the `origin` field's claim, capture writing into whichever repo the session stands in, and the scaffolded docs-lint config carrying no rules. Refs: iss-2609120447480903, iss-2609120447482255, iss-2609120447482506, iss-2609120447483698, iss-2609120447486547, iss-2609120447487070, iss-2609120447489045, iss-2609120447494818, iss-2609120450411702, iss-2609120505141653, iss-2609120511058115, iss-2609150805167646 Assisted-by: Claude:claude-fable-5-1 --- ...ithooks-pre-commit-but-leaves-git-confi.md | 24 ++++ ...ar-file-at-local-bin-abcd-is-reported-a.md | 24 ++++ ...-a-symlink-to-the-plugin-root-binary-no.md | 24 ++++ ...-deep-true-false-prompt-sits-in-the-sam.md | 24 ++++ ...nfig-gaps-docs-target-oracle-backend-re.md | 24 ++++ ...null-when-there-are-none-not-jq-gaps-fa.md | 24 ++++ ...wn-on-a-plain-plugin-install-of-a-pinne.md | 24 ++++ ...tegory-are-closed-sets-but-neither-help.md | 24 ++++ ...cks-across-a-plugin-update-v0-7-1-wrote.md | 24 ++++ ...n-emit-a-trailing-hyphen-that-its-own-s.md | 16 ++- ...int-config-carries-no-rules-so-docs-lin.md | 124 ++++++++++++++++++ 11 files changed, 355 insertions(+), 1 deletion(-) create mode 100644 .abcd/work/issues/open/iss-2609120447480903-ahoy-install-writes-githooks-pre-commit-but-leaves-git-confi.md create mode 100644 .abcd/work/issues/open/iss-2609120447482255-symlink-foreign-regular-file-at-local-bin-abcd-is-reported-a.md create mode 100644 .abcd/work/issues/open/iss-2609120447482506-path-entry-written-as-a-symlink-to-the-plugin-root-binary-no.md create mode 100644 .abcd/work/issues/open/iss-2609120447483698-ahoy-install-the-scan-deep-true-false-prompt-sits-in-the-sam.md create mode 100644 .abcd/work/issues/open/iss-2609120447486547-the-three-required-config-gaps-docs-target-oracle-backend-re.md create mode 100644 .abcd/work/issues/open/iss-2609120447487070-ahoy-json-emits-gaps-null-when-there-are-none-not-jq-gaps-fa.md create mode 100644 .abcd/work/issues/open/iss-2609120447489045-staleness-reads-unknown-on-a-plain-plugin-install-of-a-pinne.md create mode 100644 .abcd/work/issues/open/iss-2609120447494818-capture-source-and-category-are-closed-sets-but-neither-help.md create mode 100644 .abcd/work/issues/open/iss-2609120450411702-upgrade-path-self-blocks-across-a-plugin-update-v0-7-1-wrote.md create mode 100644 .abcd/work/issues/open/iss-2609150805167646-the-scaffolded-docs-lint-config-carries-no-rules-so-docs-lin.md diff --git a/.abcd/work/issues/open/iss-2609120447480903-ahoy-install-writes-githooks-pre-commit-but-leaves-git-confi.md b/.abcd/work/issues/open/iss-2609120447480903-ahoy-install-writes-githooks-pre-commit-but-leaves-git-confi.md new file mode 100644 index 00000000..704c29f0 --- /dev/null +++ b/.abcd/work/issues/open/iss-2609120447480903-ahoy-install-writes-githooks-pre-commit-but-leaves-git-confi.md @@ -0,0 +1,24 @@ +--- +schema_version: 1 +id: "iss-2609120447480903" +slug: "ahoy-install-writes-githooks-pre-commit-but-leaves-git-confi" +severity: "minor" +category: "observation" +source: "user-observation" +found_during: "ahoy-install-onboarding-2026-09-12" +origin: researcher-authored +production_mode: hand-written +--- + +ahoy install writes .githooks/pre-commit but leaves 'git config core.hooksPath .githooks' to the user, so the hook is committed but not running on the clone that just installed it. Detection knows this (hooks_path_armed=false) yet emits no gap for it. Proposal: a user-state gap that arms the clone after checking .git/hooks has no non-sample hooks the redirect would bypass, refusing with a note when it does. + +--- + +_Relocated from another repository's ledger on 2026-09-15. It was captured by an +`ahoy install` onboarding session whose working directory was a teaching-materials +repository, so the finding landed where nothing could resolve or detect it: that +tree has no installer, no plugin root and no `~/.local/bin` surface. The id, +the `found_during` stamp and the body are unchanged; only the ledger it sits in +has moved. The store resolved correctly — it wrote to the repository it was +standing in — and the reason nothing refused the write is recorded as +iss-2609120511058115._ diff --git a/.abcd/work/issues/open/iss-2609120447482255-symlink-foreign-regular-file-at-local-bin-abcd-is-reported-a.md b/.abcd/work/issues/open/iss-2609120447482255-symlink-foreign-regular-file-at-local-bin-abcd-is-reported-a.md new file mode 100644 index 00000000..a99458c7 --- /dev/null +++ b/.abcd/work/issues/open/iss-2609120447482255-symlink-foreign-regular-file-at-local-bin-abcd-is-reported-a.md @@ -0,0 +1,24 @@ +--- +schema_version: 1 +id: "iss-2609120447482255" +slug: "symlink-foreign-regular-file-at-local-bin-abcd-is-reported-a" +severity: "nitpick" +category: "observation" +source: "user-observation" +found_during: "ahoy-install-onboarding-2026-09-12" +origin: researcher-authored +production_mode: hand-written +--- + +symlink.foreign (regular file at ~/.local/bin/abcd) is reported as unresolvable with 'resolve manually' but says nothing about what the file is. Reporting size, mtime, and whether it identifies as an abcd binary (and which version) would let the user decide safely instead of inspecting by hand. The removal then needs a second full install run; a re-detect after the user clears it in the same run would save the round-trip. + +--- + +_Relocated from another repository's ledger on 2026-09-15. It was captured by an +`ahoy install` onboarding session whose working directory was a teaching-materials +repository, so the finding landed where nothing could resolve or detect it: that +tree has no installer, no plugin root and no `~/.local/bin` surface. The id, +the `found_during` stamp and the body are unchanged; only the ledger it sits in +has moved. The store resolved correctly — it wrote to the repository it was +standing in — and the reason nothing refused the write is recorded as +iss-2609120511058115._ diff --git a/.abcd/work/issues/open/iss-2609120447482506-path-entry-written-as-a-symlink-to-the-plugin-root-binary-no.md b/.abcd/work/issues/open/iss-2609120447482506-path-entry-written-as-a-symlink-to-the-plugin-root-binary-no.md new file mode 100644 index 00000000..44feb1f6 --- /dev/null +++ b/.abcd/work/issues/open/iss-2609120447482506-path-entry-written-as-a-symlink-to-the-plugin-root-binary-no.md @@ -0,0 +1,24 @@ +--- +schema_version: 1 +id: "iss-2609120447482506" +slug: "path-entry-written-as-a-symlink-to-the-plugin-root-binary-no" +severity: "major" +category: "observation" +source: "user-observation" +found_during: "ahoy-install-onboarding-2026-09-12" +origin: researcher-authored +production_mode: hand-written +--- + +PATH entry written as a symlink to the plugin-root binary (no verified artefact in the cache) is fragile by abcd's own note, yet bare 'ahoy' reports zero gaps afterwards. Immediately after a plugin update the symlink still pointed at the superseded root e3696dc while the live root was eda67ccc, and install_mode read 'pinned'. Detection should surface this as a gap (entry points at a non-current plugin root / not an owned copy) rather than only in a one-time install note; better still, provision the cache during install so the owned copy is written first time. + +--- + +_Relocated from another repository's ledger on 2026-09-15. It was captured by an +`ahoy install` onboarding session whose working directory was a teaching-materials +repository, so the finding landed where nothing could resolve or detect it: that +tree has no installer, no plugin root and no `~/.local/bin` surface. The id, +the `found_during` stamp and the body are unchanged; only the ledger it sits in +has moved. The store resolved correctly — it wrote to the repository it was +standing in — and the reason nothing refused the write is recorded as +iss-2609120511058115._ diff --git a/.abcd/work/issues/open/iss-2609120447483698-ahoy-install-the-scan-deep-true-false-prompt-sits-in-the-sam.md b/.abcd/work/issues/open/iss-2609120447483698-ahoy-install-the-scan-deep-true-false-prompt-sits-in-the-sam.md new file mode 100644 index 00000000..780fea2f --- /dev/null +++ b/.abcd/work/issues/open/iss-2609120447483698-ahoy-install-the-scan-deep-true-false-prompt-sits-in-the-sam.md @@ -0,0 +1,24 @@ +--- +schema_version: 1 +id: "iss-2609120447483698" +slug: "ahoy-install-the-scan-deep-true-false-prompt-sits-in-the-sam" +severity: "minor" +category: "observation" +source: "user-observation" +found_during: "ahoy-install-onboarding-2026-09-12" +origin: researcher-authored +production_mode: hand-written +--- + +ahoy install: the scan_deep (true/false) prompt sits in the same stdin stream as the y/N approval prompts, so the documented 'yes | abcd ahoy install' form feeds it 'y', which is silently treated as the default (false). Either accept y/n on boolean config prompts, skip the prompt when --yes or the flag is given, or refuse an unparseable answer loudly. Observed: user chose 'approve everything', scan.deep landed as false with no warning. + +--- + +_Relocated from another repository's ledger on 2026-09-15. It was captured by an +`ahoy install` onboarding session whose working directory was a teaching-materials +repository, so the finding landed where nothing could resolve or detect it: that +tree has no installer, no plugin root and no `~/.local/bin` surface. The id, +the `found_during` stamp and the body are unchanged; only the ledger it sits in +has moved. The store resolved correctly — it wrote to the repository it was +standing in — and the reason nothing refused the write is recorded as +iss-2609120511058115._ diff --git a/.abcd/work/issues/open/iss-2609120447486547-the-three-required-config-gaps-docs-target-oracle-backend-re.md b/.abcd/work/issues/open/iss-2609120447486547-the-three-required-config-gaps-docs-target-oracle-backend-re.md new file mode 100644 index 00000000..931ce22a --- /dev/null +++ b/.abcd/work/issues/open/iss-2609120447486547-the-three-required-config-gaps-docs-target-oracle-backend-re.md @@ -0,0 +1,24 @@ +--- +schema_version: 1 +id: "iss-2609120447486547" +slug: "the-three-required-config-gaps-docs-target-oracle-backend-re" +severity: "nitpick" +category: "observation" +source: "user-observation" +found_during: "ahoy-install-onboarding-2026-09-12" +origin: researcher-authored +production_mode: hand-written +--- + +The three required config gaps (docs.target, oracle.backend, repo.visibility) say 'ahoy install prompts for the value', but the flags --docs-target/--oracle-backend/--visibility exist and are the only reliable way to answer in a piped run. The fix_hint should name the flag; and a --yes run that still has to prompt for a free-text config value should say so up front rather than blocking on stdin. + +--- + +_Relocated from another repository's ledger on 2026-09-15. It was captured by an +`ahoy install` onboarding session whose working directory was a teaching-materials +repository, so the finding landed where nothing could resolve or detect it: that +tree has no installer, no plugin root and no `~/.local/bin` surface. The id, +the `found_during` stamp and the body are unchanged; only the ledger it sits in +has moved. The store resolved correctly — it wrote to the repository it was +standing in — and the reason nothing refused the write is recorded as +iss-2609120511058115._ diff --git a/.abcd/work/issues/open/iss-2609120447487070-ahoy-json-emits-gaps-null-when-there-are-none-not-jq-gaps-fa.md b/.abcd/work/issues/open/iss-2609120447487070-ahoy-json-emits-gaps-null-when-there-are-none-not-jq-gaps-fa.md new file mode 100644 index 00000000..bda38e60 --- /dev/null +++ b/.abcd/work/issues/open/iss-2609120447487070-ahoy-json-emits-gaps-null-when-there-are-none-not-jq-gaps-fa.md @@ -0,0 +1,24 @@ +--- +schema_version: 1 +id: "iss-2609120447487070" +slug: "ahoy-json-emits-gaps-null-when-there-are-none-not-jq-gaps-fa" +severity: "nitpick" +category: "observation" +source: "user-observation" +found_during: "ahoy-install-onboarding-2026-09-12" +origin: researcher-authored +production_mode: hand-written +--- + +ahoy --json emits "gaps": null when there are none, not []. jq '.gaps[]' fails on the healthy case, which is the case most scripts will hit. Same for any other list field that can be empty. + +--- + +_Relocated from another repository's ledger on 2026-09-15. It was captured by an +`ahoy install` onboarding session whose working directory was a teaching-materials +repository, so the finding landed where nothing could resolve or detect it: that +tree has no installer, no plugin root and no `~/.local/bin` surface. The id, +the `found_during` stamp and the body are unchanged; only the ledger it sits in +has moved. The store resolved correctly — it wrote to the repository it was +standing in — and the reason nothing refused the write is recorded as +iss-2609120511058115._ diff --git a/.abcd/work/issues/open/iss-2609120447489045-staleness-reads-unknown-on-a-plain-plugin-install-of-a-pinne.md b/.abcd/work/issues/open/iss-2609120447489045-staleness-reads-unknown-on-a-plain-plugin-install-of-a-pinne.md new file mode 100644 index 00000000..0e949a10 --- /dev/null +++ b/.abcd/work/issues/open/iss-2609120447489045-staleness-reads-unknown-on-a-plain-plugin-install-of-a-pinne.md @@ -0,0 +1,24 @@ +--- +schema_version: 1 +id: "iss-2609120447489045" +slug: "staleness-reads-unknown-on-a-plain-plugin-install-of-a-pinne" +severity: "nitpick" +category: "observation" +source: "user-observation" +found_during: "ahoy-install-onboarding-2026-09-12" +origin: researcher-authored +production_mode: hand-written +--- + +staleness reads 'unknown' on a plain plugin install of a pinned release, every run. For a plugin user that is noise, not signal: the skill text tells the agent to report staleness so a stale binary is never silent, so 'unknown' gets relayed on every healthy render. Either resolve it to 'current' when the pinned version matches the plugin manifest, or suppress it for pinned installs. + +--- + +_Relocated from another repository's ledger on 2026-09-15. It was captured by an +`ahoy install` onboarding session whose working directory was a teaching-materials +repository, so the finding landed where nothing could resolve or detect it: that +tree has no installer, no plugin root and no `~/.local/bin` surface. The id, +the `found_during` stamp and the body are unchanged; only the ledger it sits in +has moved. The store resolved correctly — it wrote to the repository it was +standing in — and the reason nothing refused the write is recorded as +iss-2609120511058115._ diff --git a/.abcd/work/issues/open/iss-2609120447494818-capture-source-and-category-are-closed-sets-but-neither-help.md b/.abcd/work/issues/open/iss-2609120447494818-capture-source-and-category-are-closed-sets-but-neither-help.md new file mode 100644 index 00000000..4b3228f8 --- /dev/null +++ b/.abcd/work/issues/open/iss-2609120447494818-capture-source-and-category-are-closed-sets-but-neither-help.md @@ -0,0 +1,24 @@ +--- +schema_version: 1 +id: "iss-2609120447494818" +slug: "capture-source-and-category-are-closed-sets-but-neither-help" +severity: "nitpick" +category: "observation" +source: "user-observation" +found_during: "ahoy-install-onboarding-2026-09-12" +origin: researcher-authored +production_mode: hand-written +--- + +capture --source and --category are closed sets but neither --help nor the refusal names the valid values: 'invalid source "session-observation"' with no list. An agent relaying a capture has to guess or fall back to the default. Every closed-set refusal should print the set, as --production-mode's help text already does. + +--- + +_Relocated from another repository's ledger on 2026-09-15. It was captured by an +`ahoy install` onboarding session whose working directory was a teaching-materials +repository, so the finding landed where nothing could resolve or detect it: that +tree has no installer, no plugin root and no `~/.local/bin` surface. The id, +the `found_during` stamp and the body are unchanged; only the ledger it sits in +has moved. The store resolved correctly — it wrote to the repository it was +standing in — and the reason nothing refused the write is recorded as +iss-2609120511058115._ diff --git a/.abcd/work/issues/open/iss-2609120450411702-upgrade-path-self-blocks-across-a-plugin-update-v0-7-1-wrote.md b/.abcd/work/issues/open/iss-2609120450411702-upgrade-path-self-blocks-across-a-plugin-update-v0-7-1-wrote.md new file mode 100644 index 00000000..d2e00cd5 --- /dev/null +++ b/.abcd/work/issues/open/iss-2609120450411702-upgrade-path-self-blocks-across-a-plugin-update-v0-7-1-wrote.md @@ -0,0 +1,24 @@ +--- +schema_version: 1 +id: "iss-2609120450411702" +slug: "upgrade-path-self-blocks-across-a-plugin-update-v0-7-1-wrote" +severity: "major" +category: "observation" +source: "user-observation" +found_during: "ahoy-install-onboarding-2026-09-12" +origin: researcher-authored +production_mode: hand-written +--- + +Upgrade path self-blocks across a plugin update: v0.7.1 wrote ~/.local/bin/abcd as a symlink into its own plugin root (e3696dc) with the note 're-run install from a session whose hooks have provisioned the cache to upgrade it to an owned copy'. After the plugin updated to v0.8.0 (eda67ccc), that re-run classified the symlink as symlink.foreign ('expected .../eda67ccc02eb/abcd'), unresolvable, 'ahoy refuses to clobber' — so the prescribed remedy is refused by its own predecessor's write, abcd on PATH stays at v0.7.1, and install_mode reads empty. Only a manual rm and a third install produced the owned copy. Fix: a symlink whose target is inside abcd's own plugin-cache directory (any root hash) is abcd's, not foreign — adopt and replace it; or have the first install never write the plugin-root symlink fallback at all, since it is guaranteed to go stale on the next update. Related: iss-2609120447482506. + +--- + +_Relocated from another repository's ledger on 2026-09-15. It was captured by an +`ahoy install` onboarding session whose working directory was a teaching-materials +repository, so the finding landed where nothing could resolve or detect it: that +tree has no installer, no plugin root and no `~/.local/bin` surface. The id, +the `found_during` stamp and the body are unchanged; only the ledger it sits in +has moved. The store resolved correctly — it wrote to the repository it was +standing in — and the reason nothing refused the write is recorded as +iss-2609120511058115._ diff --git a/.abcd/work/issues/open/iss-2609120452071388-the-slug-generator-can-emit-a-trailing-hyphen-that-its-own-s.md b/.abcd/work/issues/open/iss-2609120452071388-the-slug-generator-can-emit-a-trailing-hyphen-that-its-own-s.md index 015330ce..6f75da62 100644 --- a/.abcd/work/issues/open/iss-2609120452071388-the-slug-generator-can-emit-a-trailing-hyphen-that-its-own-s.md +++ b/.abcd/work/issues/open/iss-2609120452071388-the-slug-generator-can-emit-a-trailing-hyphen-that-its-own-s.md @@ -67,6 +67,19 @@ So the refusal is correct and the surface around it is not. Two sessions independently guessed values that do not exist, which is evidence about the help text rather than about the guessers. +The peer session supplied the controlled comparison that turns this from a +complaint into a diagnosis. In the same `--help` output, `--severity` names its +four members inline and `--source` and `--category` name none. Their report: +severity was right first time *because* it was named; the other two were right +only by accident. Two sessions, three flags, and the one flag that documents its +vocabulary is the one nobody got wrong. + +That isolates the cause. It is not that the vocabularies are closed, and not that +the values are unguessable — it is that two of the three do not say what they +accept, while the third, in the same help text, does. Whatever is decided about +`autonomous-hunt`, naming the members would have prevented both of the day's +wrong guesses independently of it. + ## What is owed - **Trim the separator when truncating a slug**, so the generator cannot emit @@ -91,4 +104,5 @@ text rather than about the guessers. board renders, **then** the count and the diagnostic agree about what was excluded and why. - **Given** `capture --help`, **when** a caller reads it, **then** the legal - values for `--source` and `--category` are named there. + values for `--source` and `--category` are named inline, as `--severity` + already names its four. diff --git a/.abcd/work/issues/open/iss-2609150805167646-the-scaffolded-docs-lint-config-carries-no-rules-so-docs-lin.md b/.abcd/work/issues/open/iss-2609150805167646-the-scaffolded-docs-lint-config-carries-no-rules-so-docs-lin.md new file mode 100644 index 00000000..a48739e4 --- /dev/null +++ b/.abcd/work/issues/open/iss-2609150805167646-the-scaffolded-docs-lint-config-carries-no-rules-so-docs-lin.md @@ -0,0 +1,124 @@ +--- +schema_version: 1 +id: "iss-2609150805167646" +slug: "the-scaffolded-docs-lint-config-carries-no-rules-so-docs-lin" +severity: "major" +category: "bug" +source: "agent-finding" +found_during: "peer report from a downstream repo, 2026-09-15" +origin: researcher-authored +production_mode: hand-written +found_at: "internal/core/ahoy/banlist_scaffold.go" +--- + +`abcd docs lint` reports "0 finding(s), 0 blocker(s)" in a scaffolded repository +having run no rules at all. A green result that means nothing is worse than a red +one, and this is abcd's own tool breaking abcd's own stated principle. + +## Measured, in a repository abcd prepared + +Reported by a session working in a downstream repository, with the numbers. + +Before: `abcd docs lint` returned 0 findings. Proven vacuous by appending +"Previously this was different." — a present-tense violation — to a file +**inside the configured roots**, and getting 0 findings again. + +After copying abcd's own rule set and widening the roots to the repository's +real prose surfaces: **545 findings, 535 blockers.** By rule: 419 +em-dash-in-list-item, 112 `harness/*`, 6 spelling, 5 `present_tense`, 2 +`stray_root_docs`, 1 `links_resolve`. + +Every "0 findings, 0 blockers" that repository's sessions reported to their user +this week was vacuous. + +## The cause, and the distinction the report did not draw + +`publicFamilySeed` (`internal/core/ahoy/banlist_scaffold.go:89-95`) is what a +repository with no docs-lint config inherits: + +```json +{ + "roots": ["docs", "README.md"], + "banned_tokens": [], + "rules": {}, + "exempt_paths": [], + "exempt_if_status": [] +} +``` + +The comment above it justifies the emptiness: + +> Empty is the point — abcd cannot know which names a repo may not publish, and +> seeding a ban nobody declared would fail a build over a word the maintainer +> never chose. + +**That reasoning is correct, and it covers exactly one of the two empty fields.** + +`banned_tokens` is the repository's own private names. abcd genuinely cannot know +them, and seeding one would fail a build over a word nobody chose. Empty is +right, and the scaffolded private stub's commented examples make the same +argument the same way. + +`rules` is not that. The rules are abcd's OWN Writing-Guide rules — +`links_resolve`, `present_tense`, `stray_root_docs`, `harness_leak`, the +em-dash-in-list-item token, the spelling family. They are not anybody's secrets, +they are the machine-enforced half of a guide abcd ships and documents. One +justification has been applied to two fields, and only one of them earns it. + +## Why this is the principle abcd states, broken by abcd + +`loud-staging`: "a stage that no-ops or degrades must say so; never manufacture a +false green." A lint that ran zero rules and prints "0 finding(s), 0 blocker(s)" +manufactures a false green by that definition exactly. + +The second-order cost is the one the report names: the Writing Guide labels those +rule families "machine-enforced". In a scaffolded repository that label is true of +the rule and false of the corpus, and a reader has no way to tell. They are not +misreading the guide; the guide is describing a gate that is not running. + +## Two fixes, and they are not alternatives + +1. **Say what ran.** `docs lint` must not report "0 findings" when it configured + zero rules. "no token rules configured — nothing was checked" is the honest + render, and it is the loud-staging remedy: the degradation announces itself. + This is the one that generalises, because it holds however the config got + into that state. +2. **Scaffold the rules.** Seed `rules` with the canonical families and leave + `banned_tokens` empty, since the first is abcd's and the second is the + repository's. A repository that wants a family off turns it off deliberately, + which is a decision with a record rather than an absence nobody chose. + +Do (1) regardless. (2) is the maintainer's call, and it carries a fit question +recorded below. + +## The fit question the same report raises + +Of the 545 findings, 112 were `harness/*` — the rule that refuses naming a +specific bundled tool in user-facing prose. In that repository the finding is a +false positive by construction: it is teaching material *about* those tools, so +naming them is the content. The reporting session dropped that family and +recorded why. + +"Names a specific tool" is right for abcd's published surface and wrong for a +course that teaches those tools, and 112 false positives is what a wrong rule +costs the reader's trust in the other 433. So scaffolding the rules wholesale +would import a family that cannot fit every repository. Whatever (2) does should +offer the harness family as a per-repository fit decision rather than assume it. + +## Also reported, and separable + +`ahoy` wrote `CLAUDE.md` and `AGENTS.md` into that repository as two identical +copies, where abcd's own root carries `CLAUDE.md` as a symlink to `AGENTS.md`. +Two copies drift and a symlink cannot. The scaffolded `stray_root_docs` +allowlist then named neither, so the lint flagged both files abcd had just +written. Worth its own record if the maintainer wants it separated; it is noted +here because it came from the same scaffold pass and has the same shape — the +scaffold writing something the scaffold's own gate then refuses. + +## Acceptance + +- **Given** a docs-lint configuration with no rules, **when** `abcd docs lint` + runs, **then** it says nothing was checked and does not report a finding count + that implies it was. +- **Given** a freshly prepared repository, **when** a present-tense violation is + written inside a configured root, **then** the lint finds it. From 067bbe50825d269ed824f22c0fe66ab294d7499d Mon Sep 17 00:00:00 2001 From: REPPL <77722411+REPPL@users.noreply.github.com> Date: Thu, 17 Sep 2026 07:37:50 +0100 Subject: [PATCH 2/2] docs: record the status-line build's corrections on spc-70, iss-168 and the slug issue spc-70 counted twelve elements where its own list names nine; iss-168 gains the colour ruling of 2026-09-15 (the shipped presence pair is the house yellow at 5.74:1, so polarity alone now separates it from the product thinker badge); and the slug issue carries the peer session's controlled comparison isolating why two of three closed-set flags were guessed wrong. Refs: iss-168, iss-2609120452071388 Assisted-by: Claude:claude-fable-5-1 --- ...ms-a-person-invoked-the-verb-and-is-sta.md | 96 +++++++++++++++++++ ...ing-into-whatever-repo-the-session-stan.md | 87 +++++++++++++++++ 2 files changed, 183 insertions(+) create mode 100644 .abcd/work/issues/open/iss-2609120505141653-the-origin-field-claims-a-person-invoked-the-verb-and-is-sta.md create mode 100644 .abcd/work/issues/open/iss-2609120511058115-capture-writes-a-finding-into-whatever-repo-the-session-stan.md diff --git a/.abcd/work/issues/open/iss-2609120505141653-the-origin-field-claims-a-person-invoked-the-verb-and-is-sta.md b/.abcd/work/issues/open/iss-2609120505141653-the-origin-field-claims-a-person-invoked-the-verb-and-is-sta.md new file mode 100644 index 00000000..63d2b805 --- /dev/null +++ b/.abcd/work/issues/open/iss-2609120505141653-the-origin-field-claims-a-person-invoked-the-verb-and-is-sta.md @@ -0,0 +1,96 @@ +--- +schema_version: 1 +id: "iss-2609120505141653" +slug: "the-origin-field-claims-a-person-invoked-the-verb-and-is-sta" +severity: "major" +category: "inconsistency" +source: "agent-finding" +found_during: "peer exchange on capture provenance, 2026-09-12" +origin: researcher-authored +production_mode: hand-written +found_at: "internal/core/provenance/provenance.go" +--- + +`origin: researcher-authored` states that a person invoked the verb. It is +stamped on the quoted-text route unconditionally, so a record an agent captured +carries a claim that a person captured it. + +**This record is an instance of itself.** Its own frontmatter says +`researcher-authored`, and no person invoked the verb that wrote it. + +## The claim and the write + +`internal/core/provenance/provenance.go:41` states the meaning outright: + +```go +// KindResearcherAuthored is the default for a verb a person invoked. +KindResearcherAuthored Kind = "researcher-authored" +``` + +The other two kinds are honest about their route and are derived from which +command ran: `extracted-from-record` is stamped only by `capture.Promote`, and +`contributed-by-reading /` only by `capture promote `. Both +name something structural that the command genuinely knows. + +`researcher-authored` is the default that catches everything else, and the thing +it asserts — that a person invoked it — is the one thing the command does not +know. + +## Why this is not pedantry + +Disclosure is the whole point of the field. The surface page says the two +provenance keys "are disclosure at field granularity, on the same footing as the +`Assisted-by:` trailer at commit granularity." + +The two footings disagree. At commit granularity the convention is strict, and +deliberately so: `Assisted-by:` is required, `Assisted-by: None` is the positive +declaration for human-only work, and a free-text escape was refused on the +grounds that it would reopen the omission it closes. At field granularity the +same project stamps "a person invoked this" onto records no person invoked, by +default, silently. + +Measured today, without looking far: six records captured in this session and +eleven captured by a peer session in another repository all carry +`researcher-authored`. Twenty-three including this record's own family. None was +invoked by a person. + +## What the fix is not + +Not a new flag. `origin` is "derived from which command ran and has no flag at +all", and that is the property worth keeping — a caller-supplied provenance field +is a field that gets guessed, which is exactly the failure recorded next door in +`iss-2609120452071388`, where two sessions guessed `--source` values that do not +exist in one day. + +So the value has to come from something the process knows about itself, not from +something it is told. Candidates, none obviously right: + +- A fourth kind (`agent-invoked`, or similar) selected by the same signal the + harness already uses to know it is an agent session. +- Narrowing `researcher-authored` to mean what it can actually support — filed + from quoted text, as opposed to derived from another record — and moving the + who to a separate key or dropping the who entirely. +- Leaving the value and correcting the comment, if the maintainer's reading is + that the kind names the ROUTE and the comment overstates it. That is the + cheapest fix and it should be considered honestly rather than dismissed: the + surface page's own gloss is "a draft filed from quoted text is + `researcher-authored`", which says nothing about who typed it. + +The third option is the one to rule on first, because if the comment is simply +wrong then there is no defect in the data and this record collapses to a +one-line correction. + +## Scope note + +Population is forward-only and nothing backfills a provenance stamp, so whatever +is decided applies to records written afterwards. The existing corpus keeps what +it carries, which is the right behaviour and also the reason the decision should +not sit long: every record filed meanwhile inherits the ambiguity. + +## Acceptance + +- **Given** the `origin` vocabulary, **when** a reader asks what + `researcher-authored` asserts, **then** the code comment, the surface page and + the stamped data agree. +- **Given** a record captured by an agent, **when** its `origin` is read, + **then** it does not claim a person invoked the verb. diff --git a/.abcd/work/issues/open/iss-2609120511058115-capture-writes-a-finding-into-whatever-repo-the-session-stan.md b/.abcd/work/issues/open/iss-2609120511058115-capture-writes-a-finding-into-whatever-repo-the-session-stan.md new file mode 100644 index 00000000..57a81da6 --- /dev/null +++ b/.abcd/work/issues/open/iss-2609120511058115-capture-writes-a-finding-into-whatever-repo-the-session-stan.md @@ -0,0 +1,87 @@ +--- +schema_version: 1 +id: "iss-2609120511058115" +slug: "capture-writes-a-finding-into-whatever-repo-the-session-stan" +severity: "major" +category: "ux" +source: "agent-finding" +found_during: "peer report of nine misfiled installer records, 2026-09-12" +origin: researcher-authored +production_mode: hand-written +found_at: "internal/core/capture/validate.go" +--- + +Nine findings about abcd's own installer are sitting in a teaching-materials +repository's ledger. Reported by a session working in that repository, which +could see the records but not the cause. + +## The cause, established + +**Not a containment bug.** The issue ledger is directories under the repository's +own ledger root (`internal/core/issueschema/ledgerdirs.go`), with no +root-commit-SHA keying — that keying belongs to the user-level stores +(transcripts, worktrees, voyage) and never to the in-repo ledger. So +`gitutil.CheckoutRoot` resolved correctly and wrote to the repository it was +standing in. The capturing session simply had that directory as its working +directory. + +The store worked. The distinction matters because the two hypotheses have +opposite remedies, and the reporting session could not tell them apart from where +it stood: a wrong store resolution would be a containment defect in the resolver, +and this is not that. + +## What is worth fixing anyway + +`abcd capture` will write a finding about anything into whatever repository it is +standing in, and **nothing it records has to be true of that repository**. + +`found_at` is the field that could have refused these nine. It is optional and +unvalidated: `internal/core/capture/validate.go:103` lists it among +`found_at, lapsed_at, details, suggested_fix, wontfix_reason, resolution, +promoted_to` as optional strings, and nothing checks that the path it names +exists in the tree. All nine carry it empty. + +So the failure is silent at both ends. Nothing refuses the write, and afterwards +nothing distinguishes a record about this repository from a record that merely +landed here: the reporting repository's board now reads "open 85" and leads its +recent-open list with an abcd installer defect as that repository's top open +issue. + +## The shape of a guard + +A path is checkable, and semantics are not. Do not try to judge whether a finding +is "about" this repository — that is not mechanical and a gate that guesses it +would be worse than none. + +What IS mechanical: **when `--found-at` names a repo-relative path, require it to +resolve in the tree.** That alone would have refused all nine of the misfiled +records had they named the installer files they are about, and it costs nothing +in the ordinary case where the path is real. It also makes the empty value the +only way through, which is the right place to put a nudge rather than a refusal — +a capture with no `found_at` is legitimate (a conceptual finding, a process +observation) and must stay legitimate. + +The weaker companion: the conceptual-location escape the field already documents +("optional repo-relative path or conceptual location") means a non-path value +must stay accepted, so the check fires only on something that looks like a path +and does not resolve. Getting that discrimination right is the work; refusing +every non-resolving string would break the documented use. + +## Related + +- `iss-2609120505141653` — the same nine records assert `origin: + researcher-authored`, which claims a person invoked the verb. +- `iss-2609120452071388` — `--source` and `--category` do not name their members + in help, and all nine carry the catch-all `observation`. + +Three findings from one batch of misfiled records, each a different surface +failing to hold a caller to something it knows. + +## Acceptance + +- **Given** a capture whose `--found-at` names a repo-relative path that does not + resolve in the tree, **when** the verb runs, **then** it is refused and nothing + is written. +- **Given** a capture whose `--found-at` is absent, or names a conceptual + location rather than a path, **when** the verb runs, **then** it is written as + it is today.