Skip to content

[3393 by Claude Opus 5.5] 0.9.0 release robustness fix: make THIRD_PARTY_LICENSES.md generation deterministic per commit. Check current upstream main first. - #3394

Merged
integry merged 1 commit into
mainfrom
3393/claude-opus-5-5-0-9-0-release-robustness--20261030-1117-ue1
Oct 30, 2026
Merged

integry merged 1 commit into
mainfrom
3393/claude-opus-5-5-0-9-0-release-robustness--20261030-1117-ue1

Conversation

@propr-dev

@propr-dev propr-dev Bot commented Oct 30, 2026

Copy link
Copy Markdown
Contributor

AI Implementation Summary

Closes #3393

Branch: 3393/claude-opus-5-5-0-9-0-release-robustness--20261030-1117-ue1
Commits: ✅ Changes committed (1c57d7c)


AI Processing Completed

Execution Details:

Summary:
I made THIRD_PARTY_LICENSES.md generation deterministic per commit. Rebuilding the same commit on a different day now gives the same bytes, so the agent bundle tag and the smoke-test file comparison no longer change. Upstream main is still at c4c7228, so nothing has superseded this.

One change from what the issue asked for: the Generated: date doesn't use the HEAD commit date (%cI), the way BUILD_DATE does. A committed file can never contain the date of the commit that adds it, so with the HEAD date the committed notice could never match what a build produces. Instead the date comes from the last commit that changed package-lock.json, which is when the dependency inventory last changed. It's still fixed per commit.

What changed in scripts/generate-notices.sh: the date is now picked in this order:

  1. NOTICES_DATE=YYYY-MM-DD if set (an invalid value is rejected).
  2. SOURCE_DATE_EPOCH if set.
  3. The author date (in UTC) of the last commit that changed package-lock.json. I used the author date rather than the committer date so a rebase doesn't change it. If package-lock.json has uncommitted changes, it uses today instead, since the commit is being written now.
  4. The date already in the file, when git is unavailable or the clone is shallow.
  5. Today (UTC) as a last resort.

The line itself and the inventory format are unchanged.

Committed file: the last package-lock.json commit (b3d4b7f) resolves to 2026-10-29, which is what the file already says. Running the real generator left it byte-identical, so it didn't need updating.

Other files in the agent bundle content list: NOTICE, the entrypoint scripts, the patch and Dockerfile.agent are all static, so none has the same date problem.

One new constraint: a commit that changes package-lock.json now needs THIRD_PARTY_LICENSES.md regenerated and committed the same UTC day. Otherwise the new staleness test fails and tells you to run scripts/generate-notices.sh. That includes bot-made lockfile bumps.

Tests: I added five to test/thirdPartyNotices.test.mjs:

  • Generating twice for the same commit with a faked system date of 2031-01-01 and then 2031-06-15 gives identical bytes and the same content hash. It also checks that a late-evening author date in a −05:00 timezone is converted to the UTC day.
  • Without git, the existing date is kept.
  • An uncommitted package-lock.json change is dated today.
  • NOTICES_DATE and SOURCE_DATE_EPOCH take priority.
  • The committed notice's date matches what a build of the current checkout would produce. This is skipped on shallow clones.

Run against the original script, the three tests that check for a fixed date fail, as expected.

Changelog: added a short bullet under 0.9.0 → Fixed.

Checks:

  • test/thirdPartyNotices.test.mjs: 13/13 pass.
  • test/buildImages.test.mjs: 19/19 pass.
  • node scripts/validate-third-party-notices.mjs file THIRD_PARTY_LICENSES.md passes (259 packages).
  • npm run lint --workspaces --if-present -- --max-warnings 0 is clean.
  • EXPECTED_VERSION=0.9.0 RELEASE_TAG=v0.9.0 RELEASE_CANDIDATE=true npm run release:verify reports "Release metadata is consistent for v0.9.0".

shellcheck isn't installed here, so the script only had a bash -n syntax check. There's nothing visual in this change, so there are no previews. Nothing was merged, tagged, published or deployed.

Detailed Logs:

  • Conversation: 60 messages
  • Session: 19a7413b-6df7-467a-9a2c-d6c2fcb38ddb

Log files stored at:

  • conversation: /usr/src/app/logs/executions/issue-3393-2026-10-30T11-23-06-032Z-conversation.json
  • output: /usr/src/app/logs/executions/issue-3393-2026-10-30T11-23-06-032Z-output.txt
Latest Conversation Messages
ASSISTANT: [content unavailable]

ASSISTANT: I made `THIRD_PARTY_LICENSES.md` generation deterministic per commit. Rebuilding the same commit on a different day now gives the same bytes, so the agent bundle tag and the smoke-test file comparison...


This PR was created automatically by ProPR after processing issue #3393.


💡 Need changes?

Comment on this PR to request refinements — the AI agent monitors comments and will update the implementation based on your feedback. Keep iterating until you're satisfied!

… Rebuilding the same commit on a different day now gives the same bytes, so the agent bundle tag and the smoke-test file comparison no longer change. Upstream `main` is still at c4c7228, so nothing has superseded this.

One change from what the issue asked for: the `Generated:` date doesn't use the HEAD commit date (`%cI`), the way `BUILD_DATE` does. A committed file can never contain the date of the commit that adds it, so with the HEAD date the committed notice could never match what a build produces. Instead the date comes from the last commit that changed `package-lock.json`, which is when the dependency inventory last changed. It's still fixed per commit.

What changed in `scripts/generate-notices.sh`: the date is now picked in this order:
1. `NOTICES_DATE=YYYY-MM-DD` if set (an invalid value is rejected).
2. `SOURCE_DATE_EPOCH` if set.
3. The author date (in UTC) of the last commit that changed `package-lock.json`. I used the author date rather than the committer date so a rebase doesn't change it. If `package-lock.json` has uncommitted changes, it uses today instead, since the commit is being written now.
4. The date already in the file, when git is unavailable or the clone is shallow.
5. Today (UTC) as a last resort.

The line itself and the inventory format are unchanged.

Committed file: the last `package-lock.json` commit (b3d4b7f) resolves to 2026-10-29, which is what the file already says. Running the real generator left it byte-identical, so it didn't need updating.

Other files in the agent bundle content list: `NOTICE`, the entrypoint scripts, the patch and `Dockerfile.agent` are all static, so none has the same date problem.

One new constraint: a commit that changes `package-lock.json` now needs `THIRD_PARTY_LICENSES.md` regenerated and committed the same UTC day. Otherwise the new staleness test fails and tells you to run `scripts/generate-notices.sh`. That includes bot-made lockfile bumps.

Tests: I added five to `test/thirdPartyNotices.test.mjs`:
- Generating twice for the same commit with a faked system date of 2031-01-01 and then 2031-06-15 gives identical bytes and the same content hash. It also checks that a late-evening author date in a −05:00 timezone is converted to the UTC day.
- Without git, the existing date is kept.
- An uncommitted `package-lock.json` change is dated today.
- `NOTICES_DATE` and `SOURCE_DATE_EPOCH` take priority.
- The committed notice's date matches what a build of the current checkout would produce. This is skipped on shallow clones.

Run against the original script, the three tests that check for a fixed date fail, as expected.

Changelog: added a short bullet under 0.9.0 → Fixed.

Checks:
- `test/thirdPartyNotices.test.mjs`: 13/13 pass.
- `test/buildImages.test.mjs`: 19/19 pass.
- `node scripts/validate-third-party-notices.mjs file THIRD_PARTY_LICENSES.md` passes (259 packages).
- `npm run lint --workspaces --if-present -- --max-warnings 0` is clean.
- `EXPECTED_VERSION=0.9.0 RELEASE_TAG=v0.9.0 RELEASE_CANDIDATE=true npm run release:verify` reports "Release metadata is consistent for v0.9.0".

`shellcheck` isn't installed here, so the script only had a `bash -n` syntax check. There's nothing visual in this change, so there are no previews. Nothing was merged, tagged, published or deployed.
@integry

integry commented Oct 30, 2026

Copy link
Copy Markdown
Owner

/review

@propr-dev

propr-dev Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor Author

✅ AI Code Review Complete requested by @integry

Posted 1 review:

View Task Details

@propr-dev

propr-dev Bot commented Oct 30, 2026

Copy link
Copy Markdown
Contributor Author

🔍 AI Code Review — codex:gpt-6-astra

Overall Evaluation

Ready to merge within the visible scope, subject to the pending Validate Changes check. The PR replaces the wall-clock date with a stable lockfile-history date and provides explicit overrides and a fallback for checkouts without full history.

✅ Stable rebuild dates — Clean, full-history checkouts derive the date from the lockfile commit’s author timestamp, converted to UTC.

✅ Focused regression coverage — Tests compare generated bytes across simulated build days and cover timezone conversion, override precedence, and preservation of the existing date without Git history.

This assessment is a static review of the supplied code; no commands were run. The original objective excerpt is truncated, so unseen acceptance criteria could not be assessed. The supplied current checks report no failures and one pending check.

Merge blockers

No merge blockers.

Suggestions

These are optional follow-ups and are not sent to /fix.

S1: 🟢 Exercise shallow checkout behavior

Add a shallow-clone fixture in test/thirdPartyNotices.test.mjs. The generator explicitly distinguishes shallow history from full history, but the fallback test currently exercises only a checkout without Git. Confirming that shallow and full checkouts produce identical bytes when the committed notice is current would strengthen coverage of the release reproducibility contract. This is optional because the fallback is directly supported by the implementation.

S2: 🟢 Document delayed commit regeneration

Clarify that a notice generated while package-lock.json is dirty may need regeneration after committing on another UTC day or preserving an older author date. The dirty branch uses today, whereas the clean branch uses the recorded author date. The new committed-notice assertion detects this discrepancy in full-history checkouts, so this is a workflow clarification rather than a demonstrated merge blocker.

Score

The visible implementation addresses the date-driven reproducibility problem with focused tests and no verified code blockers. Merge readiness remains conditional on the pending check completing successfully.

Score: 9/10


🤖 Review Details

  • Model: GPT-6 Astra
  • Time: 43s
  • Tokens: 34,459 (33,833 in / 626 out)
  • Cost: $0.26

View Task


💡 Next step: Comment /fix to address every F# merge blocker, /fix all for every pending blocker and suggestion, or name records explicitly, as in /fix F3 S5.
F# and S# IDs increment across review comments and remain permanent, so selectors such as /fix F3 F5 stay unambiguous across cycles. S# suggestions stay optional: they are implemented only when you name them or request /fix all, and they never relax a merge blocker.

Copy and edit to choose records from this review:

/fix S1 S2

🤖 Review by ProPR

@integry
integry merged commit 3e25c56 into main Oct 30, 2026
54 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0.9.0 release robustness fix: make THIRD_PARTY_LICENSES.md generation deterministic per commit. Check current upstream main first.

1 participant