feat: follow dependencies on every pull by default #36

Merged
claude merged 1 commits from feat/deps-by-default into main 2026-08-10 13:22:45 +00:00
Collaborator

Closes #21.

Stacked on #22 — the base branch is fix/limit-bounds-the-write, not main. Merge this after #22. It edits the same walk loop and the same argparse block in pull.py, and it depends on #22's keep predicate accounting, which it does not change.

What changes

pull.py filled depends: and followed blockers only under --deps, so the plain pull.py <n> — the only way to get a pushed issue back — answered with an empty graph. The edge cannot be recovered locally either: map.from_api writes slugs into the ## Depends on prose and never #N, so Gitea's native graph is the only source there is.

A pull now returns the unit of work: the issue and what blocks it, recursively, down to --depth (default 3).

  • --no-deps opts out of both halves and spends no request on either.
  • --deps stays accepted and is a no-op, so existing calls and /tea:sync's command tables keep working.

The cost, stated rather than hidden

The native links are now fetched once per issue instead of twice — the same links both fill depends: and steer the walk — and only for an issue that lands in the store. A closed issue that filter mode drops no longer drags its blockers in behind it.

That makes the number quotable, and pull.py's docstring quotes it: --milestone X over 50 open issues costs one list request + 50 link requests + one pair per blocker outside the milestone, where it used to cost one request flat. --no-deps is the way back to one.

Two behaviours documented as deliberate, in pull.py's docstring and in skills/sync/SKILL.md:

  • A blocker no filter selected still lands in the store, and stays outside --limit (#22's rule, unchanged): a blocker is followed because a stored issue named it, not because the filter selected it. --limit 20 can therefore leave more than 20 files behind.
  • A closed blocker is dropped in filter mode like any other closed issue, and the depends: edge to it goes with it — nothing is left pointing at a file that is not there. Key mode has no such rule and stores it.

Files

  • skills/sync/scripts/pull.py — the default, --no-deps, one link fetch per stored issue, the docstring
  • skills/sync/scripts/push.py, skills/sync/SKILL.md, AGENTS.md — docs follow the new default
  • tests/test_pull_deps_default.py — new, 19 tests
  • tests/test_checkbox_merge.py — its fake tracker now answers /dependencies, which a default pull asks for

Tests

python3 -m unittest discover -s tests — 270 tests, OK. #22's tests/test_pull_limit.py is untouched and still passes.

🤖 Generated with Claude Code

Closes #21. **Stacked on #22 — the base branch is `fix/limit-bounds-the-write`, not `main`. Merge this after #22.** It edits the same walk loop and the same argparse block in `pull.py`, and it depends on #22's `keep` predicate accounting, which it does not change. ## What changes `pull.py` filled `depends:` and followed blockers only under `--deps`, so the plain `pull.py <n>` — the only way to get a pushed issue back — answered with an empty graph. The edge cannot be recovered locally either: `map.from_api` writes slugs into the `## Depends on` prose and never `#N`, so Gitea's native graph is the only source there is. A pull now returns the unit of work: the issue and what blocks it, recursively, down to `--depth` (default 3). - `--no-deps` opts out of both halves and spends no request on either. - `--deps` stays accepted and is a no-op, so existing calls and `/tea:sync`'s command tables keep working. ## The cost, stated rather than hidden The native links are now fetched **once** per issue instead of twice — the same links both fill `depends:` and steer the walk — and only for an issue that lands in the store. A closed issue that filter mode drops no longer drags its blockers in behind it. That makes the number quotable, and `pull.py`'s docstring quotes it: `--milestone X` over 50 open issues costs one list request + 50 link requests + one pair per blocker outside the milestone, where it used to cost one request flat. `--no-deps` is the way back to one. Two behaviours documented as deliberate, in `pull.py`'s docstring and in `skills/sync/SKILL.md`: - **A blocker no filter selected still lands in the store**, and stays outside `--limit` (#22's rule, unchanged): a blocker is followed because a stored issue named it, not because the filter selected it. `--limit 20` can therefore leave more than 20 files behind. - **A closed blocker is dropped** in filter mode like any other closed issue, and the `depends:` edge to it goes with it — nothing is left pointing at a file that is not there. Key mode has no such rule and stores it. ## Files - `skills/sync/scripts/pull.py` — the default, `--no-deps`, one link fetch per stored issue, the docstring - `skills/sync/scripts/push.py`, `skills/sync/SKILL.md`, `AGENTS.md` — docs follow the new default - `tests/test_pull_deps_default.py` — new, 19 tests - `tests/test_checkbox_merge.py` — its fake tracker now answers `/dependencies`, which a default pull asks for ## Tests `python3 -m unittest discover -s tests` — 270 tests, OK. #22's `tests/test_pull_limit.py` is untouched and still passes. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
claude changed target branch from fix/limit-bounds-the-write to main 2026-08-10 13:22:02 +00:00
claude added 1 commit 2026-08-10 13:22:02 +00:00
`depends:` was filled and blockers were pulled only under `--deps`, so the
plain `pull.py <n>` — the only way to get a pushed issue back — answered
with a file whose graph was empty and an `issue_tree.py` that drew it as a
root with no blockers. The edge was not lost, but it was not asked for, and
it cannot be recovered locally: `map.from_api` writes slugs into the
`## Depends on` prose and never `#N`, so Gitea's native graph is the only
source there is.

A pull now returns the unit of work — the issue and what blocks it. `--deps`
stays accepted and does nothing, so existing calls and /tea:sync's tables
keep working; `--no-deps` is the way out and spends no request on either
half.

The cost is accepted and stated rather than hidden. The native links are now
fetched ONCE per issue instead of twice (they both fill `depends:` and steer
the walk), and only for an issue that lands in the store — a closed one that
filter mode drops no longer drags its blockers in behind it. That makes the
number quotable, and pull.py's docstring quotes it: a milestone of 50 open
issues costs one list request plus 50, where it used to cost one. In filter
mode a blocker no filter selected still lands in the store and still sits
outside `--limit`, deliberately, and both are documented; the exception is a
closed blocker, dropped like any other closed issue with the edge to it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
claude merged commit c18b16a14b into main 2026-08-10 13:22:45 +00:00
Sign in to join this conversation.