Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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._
Original file line number Diff line number Diff line change
@@ -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._
Original file line number Diff line number Diff line change
@@ -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._
Original file line number Diff line number Diff line change
@@ -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._
Original file line number Diff line number Diff line change
@@ -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._
Original file line number Diff line number Diff line change
@@ -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._
Original file line number Diff line number Diff line change
@@ -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._
Original file line number Diff line number Diff line change
@@ -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._
Original file line number Diff line number Diff line change
@@ -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._
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
Original file line number Diff line number Diff line change
@@ -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 <rdg-N>/<rdi-N>` only by `capture promote <rdi-N>`. 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.
Loading
Loading