Skip to content

fix(fathom): close six contract defects in the forge, tracker, and memory adapters - #9

Merged
crodris merged 3 commits into
mainfrom
fix/forge-tracker-memory-contract-defects
Aug 7, 2026
Merged

fix(fathom): close six contract defects in the forge, tracker, and memory adapters#9
crodris merged 3 commits into
mainfrom
fix/forge-tracker-memory-contract-defects

Conversation

@crodris

@crodris crodris commented Aug 7, 2026

Copy link
Copy Markdown
Owner

Fixes all six findings CodeRabbit raised on #8. Every one shipped in 1.1.0 and was deferred there as pre-existing, so none is a regression from the rename.

The one that actually bites

The merge-closer templates found a branch's record with an unanchored grep -rlF "$BRANCH" .fathom/. Reproduced: a plan document mentioning another branch in prose yields a false second match, the workflow takes its "refusing to guess" path, exit 1 - a red check that closes nothing. A bare substring also makes feat/tes-5 match feat/tes-50.

Matching now requires a labeled Branch line equal to the branch exactly, with the branch regex-escaped. Legacy records still resolve through the original search, which now logs the files it matched so a wrong pick is visible.

Two follow-on hazards found by testing rather than reading:

  • One issue legitimately has two records. The plan document and, in checklist mode, the task file, both named <ISSUE-REF>.md. Counting files would have refused every checklist-mode merge. The ambiguity check collapses on the file name instead; selection prefers .fathom/tasks/ by anchored prefix so a nested .fathom/.workbench/tasks/ from a botched rename cannot sort ahead of the live record.
  • pipefail. Actions' default is bash -e with pipefail off, but a consumer adding shell: bash gets pipefail, where a no-match grep aborts the job instead of taking the skip path. Every grep that can legitimately match nothing now ends in || true.

The other five

Fix Was Now
Plan-doc labels "the branch" as prose - Issue: / - Ref: / - Branch: pinned exactly - it's the only record a beads-backed issue has
Beads createTask 3 writes that fail apart, retry duplicates one bd create --external-ref --deps
verifyForge ls-remote --exit-code origin HEAD rejected a reachable remote not advertising HEAD asks only whether the remote answers
forge: local fell through toward a nonexistent forges/local.md stops with repair instructions
pushesForYou stated unscoped, so the closing commit had no publish path scoped to the review-opening push
Manual handoff partial statement says no review opened, phase is inReview, closure manual

Plus: an installed merge-closer is a copy taken at accept time, so a repo that accepted it before a template fix keeps the old copy forever. Both adapters now compare it on profile load and offer once to rewrite, recording the decline so it isn't re-asked.

Verification

Each template's run: script extracted from its YAML and executed against fixtures under GNU grep 3.12, as on ubuntu-latest:

32 cases, all passing - 8 scenarios x 2 templates x bash -e and bash -eo pipefail. Scenarios: checklist mode (plan+task), beads mode (plan only), two different issues claiming one branch (must refuse), the tes-5/tes-50 collision, legacy fallback, unknown branch, stale .workbench copy must lose, and a **Tracker** bold label.

  • shellcheck -s bash clean on both extracted scripts
  • Both YAML blocks parse as valid workflows
  • Discovery block byte-identical across templates (61 lines), now delimited by fathom:discovery-block markers so that's mechanically checkable
  • bd 0.49.0: verified --deps gives the same bd ready exclusion as bd dep add
  • SkillSpector PASS; skills/ now at zero em dashes

Three review rounds; round 1 caught the checklist-mode regression, round 2 the stale-.workbench selection, round 3 confirmed.

Note on one deferred claim

CodeRabbit's beads finding also said daemon mode drops --external-ref and --no-daemon is required. It does not reproduce on bd 0.49.0 - bd info confirmed Mode: daemon and the ref persisted through both bd create and a separate bd update. I fixed the durability half only and did not encode the unverified claim.

Breaking

None. The new - Branch: requirement is covered for old records by the legacy fallback. Minor bump to 2.1.0.

Summary by CodeRabbit

  • New Features

    • Added version 2.1.0 support and updated documentation.
    • Added optional one-time updates for outdated merge-closing workflows.
    • Improved branch and task discovery, including duplicate and ambiguous records.
    • Added clearer support for local, manual, and non-GitHub review workflows.
  • Bug Fixes

    • Improved repository connectivity checks and no-match handling.
    • Ensured final task-state changes are pushed reliably.
    • Made task creation and dependency setup more reliable.
    • Improved merge-closing behavior across supported review and tracking workflows.

…mory adapters

All six shipped in 1.1.0 and were deferred from the rename PR as
pre-existing. None is a regression from that rename.

Merge-closer branch matching (trackers/asana.md, trackers/linear.md).
The templates searched every record under .fathom/ with an unanchored
grep -rlF for the branch name. A plan document that mentions another
branch in prose produced a false second match, so the workflow took its
refusing-to-guess path and exited 1, closing nothing; a bare substring
also made feat/tes-5 match feat/tes-50. Matching now requires a labeled
Branch line whose value equals the branch exactly, with the branch
regex-escaped before use. Records written before that line was required
still resolve through the original search, which now logs the files it
matched so a wrong pick is visible.

One issue legitimately has two records, the plan document and, in
checklist mode, the task file, both named <ISSUE-REF>.md. The ambiguity
check therefore collapses on the file name; counting files would have
refused every checklist-mode merge. Selection prefers .fathom/tasks/ by
anchored prefix, so a nested .fathom/.workbench/tasks/ left by a botched
rename cannot sort ahead of the live record.

Every grep in the block that can legitimately match nothing now ends in
|| true. Actions' default shell is bash -e with pipefail off, but a
consumer who adds shell: bash gets pipefail, where a no-match grep would
abort the job instead of taking the skip path.

Plan document labels (conventions.md). The plan document is the only
record the merge-closer has on a beads-backed issue, so its Issue, Ref,
and Branch labels are now pinned exactly. A natural-reading label such as
Issue link or Tracker URL matches neither grep, and the workflow then
exits successfully having closed nothing.

Beads createTask durability (memory/beads.md). Task creation, external
ref, and dependency edges were three writes that could fail apart,
leaving an unidentifiable orphan that the retry could not recognize, so
it created a duplicate. It is now one bd create carrying --external-ref
and --deps. Verified on bd 0.49.0 that --deps produces the same bd ready
exclusion as bd dep add.

Forge preflight (forges/generic-git.md). verifyForge used
git ls-remote --exit-code origin HEAD, which fails on a reachable remote
that does not advertise HEAD and so rejected a working forge. It now
asks only whether the remote answers; the branch-specific check stays in
resolveBase.

Adapter resolution (forges.md). forge: local with no .fathom/forge.md
fell through toward a nonexistent forges/local.md. It now stops with
repair instructions, as does a bundled adapter named by the profile that
does not exist.

Push ownership (forges.md, execute/SKILL.md, forges/TEMPLATE.md).
pushesForYou was stated unscoped, but the final closing commit happens
after openReview, so on such an adapter it had no defined publish path
and would sit local while the review read as complete. The capability is
now scoped to the review-opening push.

Manual-handoff reporting (forges/generic-git.md). The handoff now states
all three facts together: no review was opened, the tracker phase is
still inReview, and closure is manual.

Also: an installed merge-closer is a copy taken at accept time, so a
repository that accepted it before a template fix would keep the old copy
forever. Both adapters now compare the installed workflow against the
current template on profile load and offer once to rewrite it, recording
the decline so the offer is not repeated.

The discovery block carries fathom:discovery-block markers so its
byte-identity across the two templates is mechanically checkable.

Verified by extracting each template's run: script from its YAML and
running it against fixtures under GNU grep 3.12: 32 cases, both
templates, under bash -e and bash -eo pipefail, all passing. Covers
checklist mode, beads mode, genuine two-issue ambiguity, the prefix
collision, the legacy fallback, the stale .workbench copy, and an
unknown branch. shellcheck clean on both.

Minor bump: behavior changes and a newly required plan-document field,
all backward compatible.
@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 34e7ca10-e3df-42be-9c5a-5ec51b799340

📥 Commits

Reviewing files that changed from the base of the PR and between 3795180 and 0f9c998.

📒 Files selected for processing (1)
  • docs/fathom.md

📝 Walkthrough

Walkthrough

The PR updates the Fathom plugin to version 2.1.0 and refines plan metadata, merge-closer workflow updates, tracker file discovery, forge lifecycle guidance, manual handoffs, and Beads task creation.

Changes

Fathom workflow updates

Layer / File(s) Summary
Plugin release metadata
.claude-plugin/marketplace.json, .claude-plugin/plugin.json, README.md
Plugin version references change from 2.0.0 to 2.1.0.
Plan metadata and workflow synchronization
docs/fathom.md, skills/fathom-shared/conventions.md, skills/fathom-shared/trackers/*
Plan files require separate Issue, Ref, and Branch labels. Merge-closer workflows can be checked against current templates and rewritten once, with declined rewrites recorded.
Tracker plan discovery
skills/fathom-shared/trackers/asana.md, skills/fathom-shared/trackers/linear.md
Asana and Linear discovery use labeled branch matching, legacy fallback, filename deduplication, task-file preference, and safe no-match handling.
Forge lifecycle and review handoff
skills/execute/SKILL.md, skills/fathom-shared/forges/*
Forge guidance separates adapter pushes during review creation from caller pushes for later commits. It also defines adapter resolution, remote verification, stable review IDs, and manual handoff states.
Atomic Beads task creation
skills/fathom-shared/memory/beads.md
Beads task creation passes optional external references and dependencies in the initial command and documents retry behavior.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant MergeCloser
  participant InstalledWorkflow
  participant PlanTaskFiles
  participant Tracker
  MergeCloser->>InstalledWorkflow: compare discovery block
  MergeCloser->>PlanTaskFiles: find labeled Branch and tracker labels
  PlanTaskFiles-->>MergeCloser: resolve preferred matching file
  MergeCloser->>Tracker: extract labeled tracker reference
Loading

Possibly related PRs

  • crod951/skills#5: Introduced related execute, tracker merge-closure, and Beads task-memory guidance.
  • crod951/skills#6: Changed the same Asana and Linear merge-closer workflows.
  • crod951/skills#7: Introduced related forge-adapter, push-ownership, and review-handling guidance.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the primary change: fixing contract defects across the forge, tracker, and memory adapters.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/forge-tracker-memory-contract-defects

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@skills/fathom-shared/forges.md`:
- Around line 21-28: Update the openReview push-ownership contract in forges.md
so the adapter owns the opening push only when pushesForYou is true; when false,
the caller must push before invoking openReview. Align the generic adapter’s
openReview documentation with this conditional rule, while preserving the
separate requirement that callers push later commits.

In `@skills/fathom-shared/trackers/asana.md`:
- Around line 182-193: Update the task-file selection logic in
skills/fathom-shared/trackers/asana.md (lines 182-193) and
skills/fathom-shared/trackers/linear.md (lines 171-182) to prefer matches under
.fathom/tasks/, then .fathom/plans/, before falling back to the first sorted
match; preserve the no-match handling and grep safeguards in both templates.
- Around line 105-106: Update the parity validation covering the discovery and
ref-extraction blocks in skills/fathom-shared/trackers/asana.md:105-106 and
skills/fathom-shared/trackers/linear.md:88-89. Normalize and compare the blocks
while allowing only the marker filename, TASK_FILE/FILE, task/issue wording, and
wrong issue/wrong one differences; reject any other drift and keep both
templates synchronized.

In `@skills/fathom-shared/trackers/linear.md`:
- Line 29: Update the shared guidance in the non-GitHub forge arrangement rule
to document both skip conditions: `ciHooks: false` and any resolved forge other
than GitHub, including the `merge-closer: none (forge is not GitHub)` outcome.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9dd3413a-9e6a-4f71-b207-e4e978f69fcb

📥 Commits

Reviewing files that changed from the base of the PR and between 5cd0327 and 6803349.

📒 Files selected for processing (13)
  • .claude-plugin/marketplace.json
  • .claude-plugin/plugin.json
  • README.md
  • docs/fathom.md
  • skills/execute/SKILL.md
  • skills/fathom-shared/conventions.md
  • skills/fathom-shared/forges.md
  • skills/fathom-shared/forges/TEMPLATE.md
  • skills/fathom-shared/forges/generic-git.md
  • skills/fathom-shared/forges/github.md
  • skills/fathom-shared/memory/beads.md
  • skills/fathom-shared/trackers/asana.md
  • skills/fathom-shared/trackers/linear.md

Comment thread skills/fathom-shared/forges.md Outdated
Comment thread skills/fathom-shared/trackers/asana.md Outdated
Comment thread skills/fathom-shared/trackers/asana.md
Comment thread skills/fathom-shared/trackers/linear.md
Four findings, all valid.

Record selection could still pick a stale copy. Anchoring on
.fathom/tasks/ covered the case where a live task file exists, but the
final fallback took the first sorted match, and .fathom/.workbench/
sorts ahead of .fathom/plans/. A beads-backed issue in a repository left
holding a nested .workbench/ from a botched rename therefore closed
against the stale record. Reproduced, then fixed by preferring
.fathom/plans/ before the unanchored fallback. Verified both templates
now select the live record under bash -e and bash -eo pipefail.

Push ownership was stated unconditionally. The contract said openReview
owns the push that opens the review, while pushesForYou: false means the
caller pushes first, which is what both bundled adapters declare and
what execute does. An agent reading only that sentence would skip the
push or perform it twice. Ownership is now stated as conditional on the
capability in both directions, and generic-git.md no longer reads as
though its openReview performs the push.

The documented parity allowlist for the two discovery blocks listed
three differences where there are four.

docs/fathom.md gave ciHooks: false as the only reason the merge-closer
question is skipped. linear.md also skips it on any non-GitHub forge,
since both closure paths are GitHub-specific.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/fathom.md`:
- Around line 360-368: Update the closure-mechanism documentation around the
“sweep is the only mechanism” claim to account for the resolved adapter’s
reviewLookup capability. State that the sweep-only path applies only when
reviewLookup is by-id; when it is none, document that no automated closure is
available. Keep the existing GitHub forge and CI-hook conditions for the
integration and merge-closer options.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 99eb6d3c-8024-46ce-a8dd-2cfc9e831016

📥 Commits

Reviewing files that changed from the base of the PR and between 6803349 and 3795180.

📒 Files selected for processing (5)
  • docs/fathom.md
  • skills/fathom-shared/forges.md
  • skills/fathom-shared/forges/generic-git.md
  • skills/fathom-shared/trackers/asana.md
  • skills/fathom-shared/trackers/linear.md
🚧 Files skipped from review as they are similar to previous changes (4)
  • skills/fathom-shared/forges.md
  • skills/fathom-shared/forges/generic-git.md
  • skills/fathom-shared/trackers/linear.md
  • skills/fathom-shared/trackers/asana.md

Comment thread docs/fathom.md
…g it always covers non-GitHub forges

An adapter declaring reviewLookup: none, which includes the bundled
generic-git fallback, cannot look a review up by id, so the sweep does
not run there. The three closure mechanisms therefore do not cover every
forge between them: where none applies there is no automated closure at
all, and the doc now says that instead of leaving the sweep to imply it.
@crodris
crodris merged commit 280bb22 into main Aug 7, 2026
3 checks passed
@crodris
crodris deleted the fix/forge-tracker-memory-contract-defects branch August 7, 2026 22:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant