Skip to content

fix(changelog): one section per repo, so shared submodules aren't repeated - #1461

Open
ErikBjare wants to merge 5 commits into
masterfrom
fix/changelog-flatten-shared-submodules
Open

ErikBjare wants to merge 5 commits into
masterfrom
fix/changelog-flatten-shared-submodules

Conversation

@ErikBjare

Copy link
Copy Markdown
Member

The v0.14.0b8 release notes list the same four aw-webui commits three times: aw-webui is a submodule of aw-server, aw-server-rust and aw-tauri, and build_changelog.py renders the submodule tree recursively, so each parent gets its own full aw-webui section. The script's own docstring told you to fix this by hand ("Remove duplicate aw-webui entries"), which is what the v0.12.0/v0.13.0 notes were: a flat, deduplicated list.

What changed

Instead of rendering while walking, the tree is collected first (collect_repos), keyed by repo name, then rendered flat: one section per repo, ordered by repo_order, root first. That is the shape the hand-edited v0.13.0 notes have.

Side effects, all wanted:

  • a parent whose only change was a submodule bump drops out entirely, instead of leaving an empty ## 📦 aw-server header (as in b8)
  • repo_order gained the repos added since it was written (aw-tauri, aw-notify, aw-watcher-input, awatcher, media), so they no longer land in arbitrary order
  • the dead remove_duplicates FIXME ("Doesn't work, messy af") is gone — it was never called

When the parents are out of sync

This is the interesting case, and the reason for not simply keeping the first occurrence. Until now the parents happened to agree (a build(deps): updated submodules commit bumps them together), but they disagree whenever one parent is bumped and another isn't — which is exactly the case where dropping a section would hide real changes.

So a shared submodule is rendered once with the union of the ranges — git log tip1 tip2 --not base1 base2, i.e. everything new in any parent — and the section carries a warning naming what each parent pinned:

⚠️ The parents of aw-webui point at different commits (aw-server22cb53b, aw-server-rust22cb53b, aw-tauri5d5378a), so the changes below are the union of all of them.

A desync becomes visible in the release notes (and a logger.warning during generation) instead of being silently resolved to one parent's range. Nothing is dropped, nothing is double-counted.

Each parent has its own clone of the submodule, so a sibling's commits may not exist in any given one. The log therefore runs in whichever checkout resolves the most pointers, and anything still unresolvable gets its own note:

⚠️ Could not resolve the commits pinned by aw-tauri9837996 in any local checkout of aw-webui, so those changes are missing below. Run git submodule update --init --recursive and regenerate.

Verification

  • regenerated the v0.14.0b8 notes from a checkout of the tag: one aw-webui section instead of three, no empty aw-server header, 81 lines instead of 107, no commit lost
  • forced the three aw-webui pointers out of sync (lagging parent, parent ahead with a local-only commit, and a pair that no single clone can resolve) and checked the union + warnings
  • ran the generator against ErikBjare/gptme (no submodules): output is byte-identical to master's
  • new scripts/tests/test_build_changelog.py builds a throwaway bundle/server/server-rust/webui tree and covers dedup, flat ordering, the dropped parent, the out-of-sync union + warning, and the absence of a warning when in sync; run in CI by .github/workflows/changelog-tests.yml

The remaining manual step for a release is what it always was, and the docstring now says so: writing the ## Summary highlights.

…eated

aw-webui is a submodule of aw-server, aw-server-rust and aw-tauri, and the
generator walked the tree recursively, so it got a full section under each of
them: the v0.14.0b8 notes list the same four aw-webui commits three times.
Cleaning that up by hand is what the script's docstring told you to do, and
what was done for v0.12.0/v0.13.0.

Collect the submodule tree first, keyed by repo name, then render one flat
section per repo in `repo_order` (which is what the hand-edited v0.13.0 notes
look like). Parents that only bumped a submodule now drop out entirely instead
of leaving an empty aw-server header.

When parents point at *different* commits of a shared submodule, the section
covers the union of the ranges (`git log tip1 tip2 --not base1 base2`) and
carries a warning naming what each parent pinned, so a desync shows up in the
release notes instead of silently picking one parent's range. Since each parent
has its own clone, the log runs in whichever checkout resolves the most
pointers, and anything unresolvable is called out with a hint to
`git submodule update --init --recursive`.

Also drops the dead `remove_duplicates` FIXME (never called, "doesn't work"),
adds the newer repos to `repo_order`, and covers the above with tests on a
throwaway nested-submodule repo. Output for repos without submodules (gptme) is
byte-identical.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 19, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-19T19:47:16.009230Z 3ff2132 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

Comment thread scripts/build_changelog.py Fixed
@greptile-apps

greptile-apps Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 4/5

The PR is not yet safe to merge because an adoption-only submodule change can generate release notes containing the repository’s complete history.

Findings

  1. P1 Adoption replays full history

Summary

The PR flattens recursive submodule changelog generation into one ordered section per repository and merges commit ranges for repositories shared by multiple parents.

  • Collects repository pointers and current parent pins before rendering.
  • Deduplicates shared submodule sections and warns about inconsistent or unresolvable pins.
  • Adds focused temporary-Git-repository tests and a GitHub Actions workflow.
  • The previous combined-range omission and converged-pointer warning findings are fixed in the current code.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  Root[Root repository range] --> Collect[Collect changed repository pointers]
  Root --> Pins[Collect current submodule pins]
  Collect --> Merge[Merge entries by repository name]
  Pins --> Merge
  Merge --> Resolve[Select checkout and resolve ranges]
  Resolve --> Render[Render one ordered section per repository]
  Resolve --> Warn[Emit pin mismatch or missing-commit warnings]
Loading

Reviews (3) · Last reviewed commit: "fix(changelog): bound a new pin instead ..."

Comment thread scripts/build_changelog.py Outdated
Comment thread scripts/build_changelog.py Outdated

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 23c9ef05e8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/build_changelog.py Outdated
Comment thread scripts/build_changelog.py
Comment thread scripts/build_changelog.py Outdated
@TimeToBuildBob

TimeToBuildBob commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

🤖 AI code review

This PR reworks the changelog generator to collect the submodule tree into a flat, per-repo structure before rendering, so a submodule shared by several parents (aw-webui) is rendered once with the union of its commit ranges and a warning when parents pin different commits. It adds a new test suite and a GitHub Actions workflow to run it, and removes the dead remove_duplicates helper.

Needs a look — P2 only

Confidence 4/5

2 findings · ⚠️ 2 P2

⚠️ P2 mediumscripts/build_changelog.py:421

In _log_commits, the pretty format string is format:'%h%x09%ct%x09%an%x09%ae%x09%s' and each line is split with line.split("\t"). The commit subject (%s) is the last field, so a subject containing a tab character would cause the split to produce more than 5 fields, and the unpacking _id, timestamp, _author, email, msg = line.split("\t") would raise a ValueError, aborting the whole changelog generation. Git commit subjects can contain tabs (they are not prohibited), so a single commit with a tab in its subject would crash the script. The old code had the same issue, but this PR rewrites the logging path and the new tests never exercise a subject with a tab. The fix is to split with maxsplit=4 or to use a more robust delimiter.

_id, timestamp, _author, email, msg = line.split("\t", maxsplit=4)

How this was verified: Checked the pretty format at line 413 and the split at line 421. Git allows tabs in commit messages; git log --pretty=format:%s does not escape them. The old code at the removed lines had the same split, but this PR's new _log_commits is the current path and the tests do not cover it.

⚠️ P2 mediumscripts/build_changelog.py:353

In _sync_notes, the out-of-sync warning is based on repo.out_of_sync, which compares the set of pin.commit values. The pins are collected by collect_pins, which reads the current index of each checkout. However, collect_pins only records pins for submodules that are actually checked out (via _is_checkout). If a parent's submodule is deinitialized (as in the test test_uninitialized_submodule_does_not_derail_pin_collection), that parent's pin is not recorded, so the warning may be missing even though the parent's gitlink in the superproject's index pins a different commit. The test only checks that the deinitialized submodule doesn't derail collection, not that the warning is correct. In a real release, if a parent's submodule is not initialized, the changelog will not warn about that parent being out of sync, even though the release notes should. The fix would be to read pins from the superproject's index (git ls-files --stage) rather than from the submodule's own checkout, but the current design relies on the checkout being present.

How this was verified: Checked _submodule_pins which returns [] if _is_checkout is false. In the test, after deinit, the server submodule is not a checkout, so its pin is not recorded. The test only asserts that 'server' is in repos and './' is not, not that the pin is present.

2 advisory findings (summary-only, not scored)

These P2 guard, heuristic, trade-off, or documentation claims are retained for judgment without opening review threads.

⚠️ P2 mediumscripts/build_changelog.py:431

In _pick_by_ancestry, the function is called with newest=False to pick the oldest base and newest=True to pick the newest tip. The implementation sets older, newer = (best, ref) if newest else (ref, best). When newest=False, it sets older=ref, newer=best, then checks git merge-base --is-ancestor older newer, i.e. whether ref is an ancestor of best. If true, it sets best = ref. This means it replaces best with ref when ref is older than best, so it ends up with the oldest ref. That is correct for newest=False. For newest=True, it sets older=best, newer=ref and checks whether best is an ancestor of ref; if true, it sets best = ref, so it ends up with the newest. That is also correct. However, the function falls back to the first ref when the refs are not ancestrally related (e.g., divergent branches). In that case, for newest=False it returns the first ref, which may not be the oldest by commit date. The changelog's full_history_url and the bounding logic rely on this to pick the oldest base and newest tip. If the parents' ranges are divergent (which is exactly the out-of-sync case this PR is designed to handle), _pick_by_ancestry may pick a base that is not the true oldest, causing the compare URL to be wrong and possibly excluding some commits from the union. The tests only use linear histories, so this is not caught. The fix would be to use git merge-base to find the common ancestor or to compare commit dates, but the current behavior is a heuristic that can misbehave on divergent histories.

How this was verified: Traced the logic for both newest values. The function only uses ancestry, not commit date, and falls back to the first ref when no ancestry relation exists. The PR's own description mentions parents can be out of sync, which often means divergent branches.

⚠️ P2 mediumscripts/tests/test_build_changelog.py:64

The new test suite uses git submodule add with relative paths (../{name}) and relies on protocol.file.allow=always. This works on Linux CI, but the tests are not hermetic with respect to the git version: older git versions may not support -b master on git init (it was added in git 2.28), and the workflow pins Python 3.9 but not a git version. The GitHub Actions runner's git is recent enough, but a developer running the tests locally on an older git would see failures unrelated to the code. This is a test-environment dependency, not a defect in the assertions themselves.

How this was verified: Checked the git init invocation; -b was introduced in git 2.28. The workflow does not pin git.

Files changed (3) — the diff as I read it
  • .github/workflows/changelog-tests.yml — Adds a workflow that runs the new changelog generator tests on PRs touching the script or tests and on pushes to master.
  • scripts/build_changelog.py — Replaces recursive rendering with collect_repos/collect_pins/summary_repos, adds Pointer/Pin/Repo dataclasses, and adds helpers for resolving ranges and logging unions.
  • scripts/tests/test_build_changelog.py — Adds a test suite that builds miniature git trees with shared submodules and asserts single sections, union behavior, warnings, and edge cases.
Previous review passes
commit score findings engine when
23c9ef05e88c 2/5 2 llm 2026-09-19 11:27 UTC
472fbf07576e 3/5 3 llm 2026-09-19 17:42 UTC
b9fe8355a7fa 3/5 2 llm 2026-09-19 19:02 UTC

Reviewed 3ff21329ca06 · openrouter/deepseek/deepseek-v4-flash-0731 · llm engine · 81s · about this reviewer

Maintainer commands

@TimeToBuildBob review (own line) — fresh review · @TimeToBuildBob fix — a worker acts on the findings. Once per comment; 👀 = received.

Comment thread scripts/build_changelog.py Outdated
Comment thread scripts/build_changelog.py Outdated
… bumped

Review findings on the first commit, all three of them real:

- `tip1 tip2 --not base1 base2` is not the union of the ranges. A commit in one
  parent's range gets dropped if another parent's *base* already contains it,
  which is exactly what happens when the parents started the release on
  different commits of the shared submodule. Log each range on its own and merge
  by commit id instead (ordering by commit date once there is more than one).
- `git submodule summary` only reports submodules that moved, so the most likely
  desync — one parent bumped, another forgotten — produced no warning at all,
  because the forgotten parent was never collected. Read the current pins from
  the index separately (`collect_pins`), so every parent that vendors the repo
  is named in the warning whether or not it moved.
- out-of-sync now compares those pins rather than whole ranges, so parents that
  started apart and converged no longer get a warning claiming they differ while
  showing identical commits.

Also: `_pick_checkout` returns None instead of a path that may not exist (an
uninitialized submodule used to crash the run), and the repeated `hidden > 1`
test CodeQL flagged is gone.

Two new tests cover the dropped-commit and never-bumped cases; both fail on the
previous commit. Regenerating v0.14.0b8 gives byte-identical output to before,
as does gptme (no submodules).

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 472fbf0757

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/build_changelog.py Outdated
Comment thread scripts/build_changelog.py
Comment thread scripts/build_changelog.py
Comment thread scripts/build_changelog.py
…lvable ones

Second review round, two more real ones:

- a parent that adopts a submodule mid-release has no range of its own, so
  merging its unbounded log with another parent's bump replayed the submodule's
  entire history into the release notes. Prefer the bounded ranges when there
  are any; a submodule that is new to *every* parent still lists everything, as
  before.
- when no checkout can resolve a repo's pointers (a shallow clone whose base
  commit is gone, say), the repo vanished from the notes with only a log line.
  It now gets its section and the "could not resolve" note, which was the point
  of that note.

Both cases fail on the previous commit.
@ErikBjare

Copy link
Copy Markdown
Member Author

@greptileai review

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c58d278639

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/build_changelog.py Outdated
Comment thread scripts/build_changelog.py Outdated
Comment thread scripts/build_changelog.py Outdated
… on filtered repos

Third review round:

- bounding the newly-vendored parent by dropping its range threw away commits
  that only its pin has (it may adopt the repo at a commit ahead of what the
  other parents bumped to). Bound it by where the parents that already had it
  started the release instead, so every pinned commit is covered — which is what
  the out-of-sync note promises.
- a repo whose only commits this release are filtered (build/ci) has no entries,
  so the section was dropped along with its out-of-sync warning — exactly when
  the mismatch is least visible otherwise. A repo that moved now keeps its
  section for the warning alone; one that didn't move stays silent.

Both fail on c58d278.
@ErikBjare

Copy link
Copy Markdown
Member Author

@greptileai review

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b9fe8355a7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/build_changelog.py Outdated
Comment thread scripts/build_changelog.py Outdated
Comment thread scripts/build_changelog.py Outdated
Comment thread scripts/build_changelog.py
Comment thread scripts/tests/test_build_changelog.py
…ninitialized dirs

Fourth review round:

- an uninitialized submodule leaves an empty directory, and git commands run there
  answer for the *superproject*: `git ls-files --stage` reports the gitlink itself
  as "./", so `collect_pins` recursed on ever-longer paths (the string-keyed `seen`
  never matched) until it hit RecursionError. Check that a directory is its own
  checkout before reading pins from it.
- adoption with no accompanying bump left `bounded` empty, so the unbounded range
  survived and replayed the repo's whole history. Bound the pins by where the
  parents that already had it sit, whether or not anything was bumped; a repo new
  to every parent still lists everything.
- ranges are now resolved one at a time, each in a checkout that holds it
  (preferring the clone of the parent it came from), rather than all from one
  "best" checkout. With shallow clones holding disjoint histories, commits that
  are available somewhere are no longer reported as unavailable everywhere.

Three of the four are Codex's, the adoption one is Greptile's. All fail on b9fe835.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3ff21329ca

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

for pointer in pointers:
if pointer.commit_range in resolved:
continue
path = _find_checkout([pointer.path] + checkouts, pointer.commit_range)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Reject uninitialized paths when resolving empty ranges

When a newly added submodule is uninitialized, its normalized range is ("", ""), so _resolvable_in succeeds vacuously and this prepended raw pointer.path is selected even though it was excluded from checkouts by _is_checkout. Running git log inside that empty directory then resolves to the enclosing superproject and attributes its entire history to the new submodule (or fails if the directory is absent). Fresh evidence beyond the earlier pin-collection fix is that _resolve_ranges reintroduces the unchecked path; restrict every candidate here to verified checkouts.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Fixed together with the other fresh Codex findings in TimeToBuildBob/activitywatch@39783ce. _resolve_ranges now passes only _is_checkout-verified candidates, so an empty uninitialized gitlink directory cannot win vacuously and fall back to the superproject. Added a focused regression test.

starts = [since for _, (since, _) in bounded] or [
pin.commit for pin in repo.pins
]
if len(starts) > 1 or bounded:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep repositories newly added by every parent unbounded

When two parents both add the same repository during this release, bounded is empty but starts contains one pin per parent, so this condition treats the repository as if it had pre-existing parents. If both parents pin the same commit, the generated ranges are pin...pin and the entire repository section disappears; with different pins, all history through the selected base is omitted. Fresh evidence beyond the prior single-parent-adoption fix is the all-parents-new case; when there is no bounded range, retain one unbounded range so the newly introduced repository's history is listed.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Fixed in TimeToBuildBob/activitywatch@39783ce. The all-parents-new case now stays unbounded when every current pin agrees, while adoption into an already-vendored but unmoved repo still uses the older existing pin as its boundary. Added test_repo_new_to_every_parent_keeps_full_history; focused suite is 17/17.

Comment on lines +505 to +507
path = _find_checkout([pin.path] + checkouts, (base, pin.commit))
if path:
bounded.append((path, (base, pin.commit)))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Report newly vendored pins that cannot be bounded

When an existing parent's bounded range is in one shallow checkout and a newly vendored pin is in another checkout lacking that range's base, _find_checkout returns None here and the new pin is silently skipped. The original empty range was already considered resolved and _sync_notes ran before this derived range was attempted, so the release notes neither include the pin's unique commits nor warn that they are missing. Fresh evidence beyond the earlier per-range resolution fix is that these synthetic bounded ranges are created afterward; failed bounds must be carried into the unresolved diagnostics.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Fixed in TimeToBuildBob/activitywatch@39783ce. Failed synthetic (base, new-pin) resolutions are now appended to the unresolved diagnostics instead of silently skipped, and notes are rebuilt after deriving those ranges. Added test_unresolvable_synthetic_adoption_range_gets_warning; full focused suite passes 17/17.

f"git log {rev} --no-decorate --pretty={pretty}", cwd=path
).split("\n"):
if line:
_id, timestamp, _author, email, msg = line.split("\t")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ P2 — In _log_commits, the pretty format string is format:'%h%x09%ct%x09%an%x09%ae%x09%s' and each line is split with line.split("\t"). The commit subject (%s) is the last field, so a subject containing a tab character would cause the split to produce more than 5 fields, and the unpacking _id, timestamp, _author, email, msg = line.split("\t") would raise a ValueError, aborting the whole changelog generation. Git commit subjects can contain tabs (they are not prohibited), so a single commit with a tab in its subject would crash the script. The old code had the same issue, but this PR rewrites the logging path and the new tests never exercise a subject with a tab. The fix is to split with maxsplit=4 or to use a more robust delimiter.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same defect as the earlier thread (fp 03f198b0a170, answered at 19:46Z) — the reviewer regenerated it under a new fingerprint on 3ff2132, so it will keep reappearing while this head stands.

The fix is on TimeToBuildBob/activitywatch@39783ce (branch bob/fix-changelog-review-round4): _log_commits splits with line.split("\t", 4) plus a test_commit_subject_with_tab_does_not_break_log_parsing regression test. It is not on this PR's head because TimeToBuildBob has no write access to the upstream branch (and no ResolveReviewThread permission either), so it lands only when Erik cherry-picks that commit. Leaving this thread open is the accurate signal until then.

return repos
seen.add(path)

for name, commit in _submodule_pins(path):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ P2 — In _sync_notes, the out-of-sync warning is based on repo.out_of_sync, which compares the set of pin.commit values. The pins are collected by collect_pins, which reads the current index of each checkout. However, collect_pins only records pins for submodules that are actually checked out (via _is_checkout). If a parent's submodule is deinitialized (as in the test test_uninitialized_submodule_does_not_derail_pin_collection), that parent's pin is not recorded, so the warning may be missing even though the parent's gitlink in the superproject's index pins a different commit. The test only checks that the deinitialized submodule doesn't derail collection, not that the warning is correct. In a real release, if a parent's submodule is not initialized, the changelog will not warn about that parent being out of sync, even though the release notes should. The fix would be to read pins from the superproject's index (git ls-files --stage) rather than from the submodule's own checkout, but the current design relies on the checkout being present.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

False positive — the premise does not hold. collect_pins starts at the superproject root, and _submodule_pins(parent) reads gitlinks from the parent's index (git ls-files --stage in the parent), not from the pinned repo's own checkout. _is_checkout guards the directory being scanned (so an empty leftover directory can't answer for the superproject), not whether the pinned repository is initialized.

Reproduced locally: after git submodule deinit -f server, the root index still reports 160000 <sha> 0 server, so the root's pin is recorded and out_of_sync still sees it. The only thing a deinitialized submodule loses is deeper recursion into its own gitlinks — which is unreadable without its objects regardless, so no warning is silently dropped.

("How this was verified" states the test "only checks that the deinitialized submodule doesn't derail collection, not that the warning is correct" — true of the test, but the code path above is the reason the warning is correct anyway.)

@TimeToBuildBob

Copy link
Copy Markdown
Contributor

The remaining fix is ready but cannot be pushed to this upstream-owned branch: cherry-pick TimeToBuildBob/activitywatch@39783ce (one commit directly on 3ff2132). It addresses the four still-open review threads and adds focused regressions; the branch’s changelog suite passes 17/17. Current upstream CI is green, but the PR should not merge until that commit lands.

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.

3 participants