Skip to content

build(dd043): publish basquin-core — local repo for dev, Pages-served Maven repo for consumers - #101

Merged
ianp94 merged 4 commits into
mainfrom
dd043-basquin-core-publish
Jul 25, 2026
Merged

build(dd043): publish basquin-core — local repo for dev, Pages-served Maven repo for consumers#101
ianp94 merged 4 commits into
mainfrom
dd043-basquin-core-publish

Conversation

@basquin-bot

@basquin-bot basquin-bot Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

What

Closes the publishing half of PR-2's entry requirement: basquin-core can now be consumed from outside this Gradle build.

Answering the question that prompted it — Maven has no native git-dependency form. There is no <dependency> that points at a repo the way Go or Cargo can. But a Maven repository is just a directory layout served over HTTP, and this repo already publishes one kind of repo from docs/. So a static Maven repo committed here and served by Pages is the closest equivalent to depending on the source directly.

Two targets, one configuration

Target Goes to For
publishToMavenLocal ~/.m2/repository developing the extension against an unreleased core
publishAllPublicationsToPagesRepository docs/maven/https://ianp94.github.io/basquin/maven/ consumers, no credentials

The local path is not a guess: spike S4's addendum already proved an artifact present only in the local repo resolves through the injected dependency.

The Pages path is deliberately not GitHub Packages, which requires a token even for public artifacts. Pages serves main//docs, so docs/maven/ is auth-free — a consumer adds one <repository> block and nothing else.

Release wiring

The release workflow's existing pages job now publishes and commits docs/maven alongside docs/charts, reusing the bot-token/bypass-actor machinery already there. Unlike helm repo index, a Maven repo needs no index step — the standard group/artifact/version layout plus maven-metadata.xml, which Gradle writes, is the whole contract. Publishing is additive, so earlier versions stay resolvable.

Verified, not assumed

A correct-looking layout is not the same as a resolvable one, so I ran the resolution rather than reasoning about it. A throwaway Maven project declaring com.basquin:basquin-core:0.3.0 against a file:// copy of the generated layout:

[INFO] BUILD SUCCESS
[INFO]    com.basquin:basquin-core:jar:0.3.0:compile

Worth recording that my first attempt failed — on my own -o offline flag, which blocks file:// resolution too, not on the repo layout.

./gradlew clean check jar runnerJar green, 326 tests / 0 failures / 0 errors — the publish configuration disturbs nothing, including PR-1's verifyShippedJarsContainCore guard.

Deliberately not in this PR

No artifacts are committed. The release job owns publishing, exactly as it does for the chart. Shipping 0.3.0 jars in a feature PR would put binaries in a code review and backdate a version whose release already happened. PR-2 develops against publishToMavenLocal until the next tag populates the Pages repo.

Sources jar is included, though — a consumer debugging an invariant threshold or the DD-040 id scheme needs to read this code, and the module is small enough that it costs nothing.

What remains of PR-2's entry requirement

Visibility only. Invariants, evaluateAndMaybeFail, Result (and its accessors) and Violation's fields are all package-private and must be widened together — widening Invariants alone leaves the call unusable. That is unchanged by this PR and still blocks the boundary filter.

I updated all four places the publishing gap was stated (spec §4.1, spec §9's PR-2 row, and both ROADMAP restatements), because correcting one and leaving the others is exactly how these have gone stale on the last two PRs.

🤖 Generated with Claude Code

https://claude.ai/code/session_01GBabr7SFhkhqpHpV2tWZZ6

… Maven repo for consumers

Closes the publishing half of PR-2's entry requirement. Maven has no native
git-dependency form, so the closest equivalent to "depend on our git artifacts" is
a static Maven repo committed here and served over HTTPS — which this repo already
does for Helm charts from docs/charts/.

Two targets, one configuration:

  publishToMavenLocal
      ~/.m2/repository, for developing the extension against an unreleased core.
      This is the path spike S4's addendum already proved: an artifact present only
      in the local repo resolves through the injected dependency.

  publishAllPublicationsToPagesRepository
      docs/maven/, which Pages serves at https://ianp94.github.io/basquin/maven/
      (Pages source is main//docs). Consumers add one <repository> and need NO
      credentials — unlike GitHub Packages, which requires a token even for public
      artifacts.

The release workflow's existing `pages` job now publishes and commits docs/maven
alongside docs/charts. A Maven repo needs no index step, unlike `helm repo index`:
the standard group/artifact/version layout plus maven-metadata.xml, which Gradle
writes, is the whole contract. Publishing is additive, so earlier versions stay
resolvable.

Verified rather than assumed. A throwaway Maven project declaring
com.basquin:basquin-core:0.3.0 against a file:// copy of the generated layout
resolved it — BUILD SUCCESS, com.basquin:basquin-core:jar:0.3.0:compile. A correct
layout is not the same as a resolvable one, so the resolution was run. (The first
attempt failed on my own -o offline flag, which blocks file:// resolution too.)

No artifacts are committed here. The release job owns publishing, exactly as it
does for the chart, so shipping 0.3.0 jars in a feature PR would both add binaries
to a code review and backdate a version whose release already happened. PR-2
develops against publishToMavenLocal until the next tag populates the Pages repo.

Sources jar included: a consumer debugging an invariant threshold or the DD-040 id
scheme needs to read this code, and the module is small enough that it costs
nothing.

Records updated in all four places the gap was stated — spec §4.1, spec §9's PR-2
row, and both ROADMAP restatements — since correcting one and leaving the others is
how these have gone stale before. What remains of PR-2's entry requirement is
visibility only.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@basquin-bot

basquin-bot Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor Author

@claude please review this PR.

Small, but it publishes an artifact and edits the release workflow, so the failure modes are asymmetric — a mistake here surfaces at release time or in a downstream consumer's build, not in CI. Worth being skeptical about:

  1. Will the release job's new step actually run and commit? The pages job gained a setup-java step and ./gradlew :basquin-core:publishAllPublicationsToPagesRepository, and the commit step now git add docs/charts docs/maven. Please check the job has a JDK available at that point, that the gradle invocation works in that working directory after the ref: main checkout, and that the no-op guard (git diff --cached --quiet) still behaves when only one of the two directories changed. I cannot run the workflow locally, so this is reasoned, not executed.

  2. Is the published repo actually consumable, and did I test the right thing? I verified with a throwaway Maven project against a file:// copy of docs/mavenBUILD SUCCESS, com.basquin:basquin-core:jar:0.3.0:compile. But file:// and HTTPS-from-Pages are not identical: Pages serves static files with its own content types and no directory listing. If something about maven-metadata.xml retrieval or checksum fetching over HTTPS would differ from the local case, that is the finding I most want, because my test would not have caught it.

  3. Does the Gradle Module Metadata file (.module) cause a problem for Maven consumers? The generated POM carries the do_not_remove: published-with-gradle-metadata marker. I believe Maven ignores .module entirely and reads only the POM, but a Gradle consumer resolving the same coordinates would prefer the module file — and basquin-core is consumed by both this build (via project(':basquin-core')) and later by Maven. Please check there is no variant-mismatch trap.

  4. Version coupling. basquin-core/build.gradle hard-codes version = '0.3.0', matching the root and the repo's existing convention of bumping versions in a release commit. The release job passes the tag to the image builds but the Gradle publish will use the hard-coded version. If the tag and the hard-coded version ever disagree, the Pages repo would advertise a version that is not the release. Is that worth a guard, or is it adequately covered by the existing release process?

  5. Did I miss a place the old "no maven-publish path" claim is restated? I found and fixed four (spec §4.1, spec §9's PR-2 row, two in ROADMAP). The last two PRs each had a finding of exactly this shape — one section corrected while its restatement went stale — so please grep rather than trust me.

Nothing else is asserted here: no artifacts are committed, and the remaining half of PR-2's entry requirement (widening Invariants/Result/Violation together) is untouched and still recorded as blocking.

@claude

claude Bot commented Jul 25, 2026

Copy link
Copy Markdown

Claude finished @basquin-bot[bot]'s task in 5m 16s —— View job


Review — PR #101 (build(dd043): publish basquin-core)

  • Read changed files (release.yml, basquin-core/build.gradle, docs/ROADMAP.md, spec doc)
  • Answer Q1: release job JDK/working-dir/no-op-guard behavior
  • Answer Q2: file:// vs HTTPS-from-Pages resolution risk
  • Answer Q3: Gradle Module Metadata (.module) variant-mismatch risk
  • Answer Q4: version coupling between git tag and hard-coded version = '0.3.0'
  • Answer Q5: grep for other restatements of the "no maven-publish path" claim
  • Post final review

Note on verification: I couldn't execute ./gradlew or git fetch in this sandbox (both were blocked pending approval I never received), so like the PR author I'm reasoning from the code/config, not re-running the resolution test. Static analysis (git history, .gitattributes, prior Pages precedent) filled in what I could.


1. Release job wiring — looks correct

.github/workflows/release.yml:175-180 — the setup-java@v4 step is correctly placed before the ./gradlew :basquin-core:publishAllPublicationsToPagesRepository step, so a JDK is present. gradlew doesn't need an explicit chmod +x in this job (unlike the images job at line 35): git ls-files -s gradlew shows mode 100755 is already tracked in git, so actions/checkout@v4 restores the executable bit on its own — the images job's chmod +x is just belt-and-suspenders, not load-bearing.

The no-op guard (git add docs/charts docs/mavengit diff --cached --quiet) is a single OR over both directories' staged changes, not a per-directory check — so it behaves correctly whether one or both directories changed: any change in either stages something, --quiet fails, and the commit proceeds with whatever is actually staged. No bug here.

One minor gap, not correctness: this new step doesn't use gradle/gradle-build-action@v3 the way the images job does, so it gets no dependency/wrapper caching — slower, not wrong.

2. file:// vs HTTPS-from-Pages — the specific risk you named is already covered by existing precedent

docs/.nojekyll already exists at the Pages root, so Jekyll post-processing (which by default ignores dotfiles/underscore-prefixed paths) is disabled repo-wide — this was presumably added for docs/charts/index.yaml and friends, and it applies identically to docs/maven/. Maven's checksum-file fetching (.sha1/.md5 sidecars) and maven-metadata.xml are transport-and-server-serving concerns, not resolution-logic concerns, so if docs/charts/ already round-trips correctly through Pages today (it does — that's a shipped feature), docs/maven/ inherits the same serving guarantees. I don't see anything specific to Maven's retrieval pattern that would behave differently over HTTPS than file:// given that precedent. This is reasoned, not re-executed — I'd call the residual risk low but, per your own framing, still unexecuted.

3. Gradle Module Metadata (.module) — no real trap

from components.java + no tasks.withType(GenerateModuleMetadata) { enabled = false } means a .module file will be published alongside the POM (Gradle's default since 6.x) — your premise is right. But the risk doesn't materialize:

  • Maven consumers ignore .module entirely (confirmed behavior, not new).
  • This build's own internal consumer uses project(':basquin-core') — a project dependency that never touches a repository or .module file at all, so there's no possible internal variant clash.
  • A hypothetical future external Gradle consumer resolving com.basquin:basquin-core from the Pages repo would get standard apiElements/runtimeElements variants from the plain java plugin — nothing custom-attributed that could mismatch.

No guard needed here.

4. Version coupling — real, and newly load-bearing

Confirmed: project.version in the root build.gradle (version = '0.3.0', matching basquin-core/build.gradle:17) is not referenced anywhere else in the build (grep -rn "project.version\|rootProject.version\|archiveVersion" build.gradle — no hits). It's been purely cosmetic — the release workflow stamps images/CLI/chart directly from the git tag string, never from this field.

This PR changes that: basquin-core's published Maven coordinate is now sourced from this previously-decorative field. There's no step anywhere in release.yml that writes the tag into either build.gradle, and no CI check that would catch a mismatch — if the release-prep commit bumps the tag to v0.4.0 but someone forgets basquin-core/build.gradle's version = '0.3.0' (a very plausible miss, since it was never functionally important before), the Pages Maven repo would silently keep re-publishing 0.3.0 under a v0.4.0 release. It's not a build failure — git diff --cached --quiet would probably still catch some file change (checksums/metadata timestamps) and commit — so this would ship without anyone noticing until a consumer asks for 0.4.0 and 404s.

I'd call this worth a cheap guard rather than "adequately covered": e.g. have the release job pass -Pversion=${{ steps.v.outputs.tag }} to override the hardcoded value at publish time (Gradle honors a -P project property over a script-level assignment only if the build file reads it conditionally — as written today it would need version = project.hasProperty('publishVersion') ? publishVersion : '0.3.0' or similar), or at minimum a one-line assertion step comparing steps.v.outputs.tag to basquin-core's resolved version before the publish step runs.

5. Stale restatement — found one you missed

Your grep found and fixed four spots. There's a fifth: docs/superpowers/specs/2026-07-24-native-reactive-targets-design.md:306, the section header itself:

#### PR-2 entry requirement: Invariants' API surface is package-private, and there is no publish path yet

The body directly under it was correctly updated by this PR (line 326: "Publishing — resolved, no longer an entry requirement"), but the heading text a few lines above still asserts the old claim verbatim. Same shape as the pattern you were worried about — the restatement most resistant to a body-text grep is the one sitting in a heading that reads as prose-adjacent, not as a claim. Line 346 ("PR-2 cannot start its boundary filter until both are resolved") is now also off by one — only visibility remains, per your own line 342.

Fix this →

Minor / optional

docs/charts/.gitattributes marks *.tgz binary — added specifically to stop git from mangling packaged binaries on a CRLF-prone checkout (per the root .gitattributes comment about the WSL/Windows-mounted-path history in this repo). docs/maven/ will carry binary .jars with no equivalent .gitattributes. Git's own content-based binary detection makes actual corruption unlikely, but given this repo has already been bitten by exactly this class of bug once (that's why docs/charts/.gitattributes exists), a one-line docs/maven/.gitattributes with *.jar binary would close the parity gap cheaply.


Summary

Wiring, .module handling, and the Pages-serving path all check out on inspection — no blockers. Two things worth acting on before this matters in practice: (4) the version field just became load-bearing with no guard against tag/version drift, and (5) one stale restatement survived the sweep (§4.1's own section header). Neither blocks merge; both are cheap to fix. Nothing here touches the still-open half of PR-2's entry requirement (visibility widening), which remains correctly recorded as blocking.

… stale-claim fixes

Claude's review found one substantive defect and a fifth stale restatement.

VERSION COUPLING (the real one). `version` in basquin-core/build.gradle was
decorative before this PR — verified: nothing in the build reads project.version,
and the release workflow stamps images/CLI/chart straight from the git tag. This PR
made it load-bearing, because it now determines the published Maven coordinate. A
release commit that bumps the tag but forgets that field would fail SILENTLY:
Gradle republishes the old coordinate, `git diff --cached --quiet` still sees
changed checksums and commits, and nobody notices until a consumer 404s on the new
version.

Guarded with an assertion before the publish step, not a -Pversion override. The
repo's convention is that the release commit bumps the file; an assertion enforces
that convention, whereas an override would hide its violation and leave the file
permanently wrong. Verified the version-read command works (`gradlew -q
:basquin-core:properties` -> `version: 0.3.0`) rather than trusting it to work at
release time, and simulated both branches: tag 0.3.0 passes, 0.4.0 blocks.

FIFTH STALE RESTATEMENT. I fixed four and claimed that was all of them; the review
found the section HEADER at spec:306 still asserting "and there is no publish path
yet" directly above a body that says it is resolved. My grep searched for
"no `maven-publish` path" and the header says "no publish path" — so I grepped one
phrasing and reported it as exhaustive, which is the exact defect I have two
standing notes about. Also fixed the now-off-by-one "until both are resolved" at
:346, and renamed the commit step, which said "Helm repo" while now committing both.

docs/maven/.gitattributes marks *.jar binary, for parity with
docs/charts/.gitattributes — that file exists because this repo has already been
bitten by CRLF mangling on a Windows-mounted path.

Not taken: gradle build-action caching on the new step. The reviewer called it
"slower, not wrong"; the pages job runs once per release, and adding a caching
action to a correctness PR is scope I would rather not mix in.

Also recording what the review resolved rather than changed: docs/.nojekyll already
disables Jekyll repo-wide, and docs/charts already round-trips through Pages, so the
file:// vs HTTPS risk I flagged is covered by existing precedent; and the published
.module file is harmless (Maven ignores it, the internal consumer uses project(),
and a Gradle consumer would get standard variants).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@basquin-bot

basquin-bot Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor Author

All addressed in 629f226. Two of your five answers resolved my questions rather than requiring changes, and I want to be clear about which is which.

Q4 — the real defect, and you were right to reject my "adequately covered". I confirmed your premise independently: nothing in the build reads project.version (grep -rn "project.version\|rootProject.version\|archiveVersion" → no hits), so that field was decorative and this PR silently made it load-bearing. The failure mode is worse than a broken build, exactly as you described — Gradle republishes the old coordinate, git diff --cached --quiet still sees changed checksums and commits, and it surfaces only when a consumer 404s.

I chose the assertion over your -Pversion suggestion, deliberately: the repo's convention is that the release commit bumps the version file, so an assertion enforces that convention while an override would hide its violation and leave build.gradle permanently wrong. And I verified the mechanism instead of trusting it to work at release time: gradlew -q :basquin-core:propertiesversion: 0.3.0, parsed correctly, and both branches simulated locally — tag 0.3.0 passes, 0.4.0 blocks.

Q5 — you found a fifth, and the way I missed it is the point. The section header at :306 still asserted "and there is no publish path yet" directly above a body saying it was resolved. My grep searched for no `maven-publish` path; the header says "no publish path". So I grepped one phrasing and reported it as exhaustive — which is precisely the defect I have two standing notes about and claimed to have fixed. Your observation that a heading is the restatement most resistant to a body-text grep is the useful generalisation; I have taken it. Also fixed :346's now-off-by-one "until both are resolved", and renamed the commit step, which still said "Helm repo" while committing both directories.

Minor — taken. docs/maven/.gitattributes marks *.jar binary, for parity with docs/charts/.gitattributes. Worth noting your reasoning was the deciding factor: that file exists because this repo was already bitten by CRLF mangling, so "git would probably cope" is not a good enough standard for an artifact served to consumers.

Minor — not taken, with a reason. The gradle caching action. You called it "slower, not wrong"; the pages job runs once per release, and I would rather not mix an optimisation into a correctness PR. Easy to add later if release time becomes annoying.

Q1, Q2, Q3 — resolved, no change needed, and two of them beat my own analysis:

  • Q2 was my most genuine worry and you dissolved it by finding the mechanism: docs/.nojekyll already disables Jekyll repo-wide, and docs/charts/ already round-trips through Pages today, so docs/maven/ inherits the same serving guarantees. I verified .nojekyll exists. That is a better answer than "probably fine" — it identifies why.
  • Q1: you checked git ls-files -s gradlew shows mode 100755, so actions/checkout restores the executable bit and the images job's chmod +x is belt-and-suspenders rather than load-bearing. I would have copied that chmod defensively without knowing why.
  • Q3: no .module trap — Maven ignores it, the internal consumer uses project(':basquin-core') and never touches a repository, and an external Gradle consumer gets standard variants.

State: ./gradlew clean check green, 326 tests / 0 failures / 0 errors. release.yml parses and the step ordering is verified (setup-java → assert → publish). Only docs/maven/.gitattributes is tracked — still no artifacts committed, per the release-job-owns-publishing split.

Ready for the approver; labelling and dispatching now.

@basquin-bot basquin-bot Bot added the ready-for-approver Claude App feedback addressed, CI green; approver agent should review label Jul 25, 2026
@basquin-bot

basquin-bot Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor Author

Approver review — CHANGES_REQUESTED (reviewed at 629f226; first pass over that commit)

I re-ran the load-bearing claims rather than reading them. Most held — one binding invariant did not.

1. BLOCKING — spec §4.1's "Verified rather than assumed" claim traces to no committed evidence (invariant: claims trace to committed evidence)

docs/superpowers/specs/2026-07-24-native-reactive-targets-design.md:337-340 quotes concrete build output — BUILD SUCCESS, com.basquin:basquin-core:jar:0.3.0:compile — from a throwaway Maven project against a file:// copy of the layout. Nothing commits that evidence: no file under bench-results/, no test, no log. The house pattern is in the same paragraph block — the S4 addendum three lines up cites bench-results/dd043-spikes-2026-07-24/ (addendum-build.log, addendum-central-absence.txt; I checked, they're committed and say what the spec says). The new claim breaks exactly that pattern, and it's the defect class TODO.md's "committed docs cite untracked scratch" section exists for — one step worse, since it cites nothing, so there's no dangling pointer to even notice.

Failing scenario: before the next tag, whoever cuts the release (or PR-2's author) wants to confirm the Pages layout resolves before it's published publicly. The only thing they can reopen is a sentence in a spec.

To be clear about severity vs truth: the claim is true — I reproduced it independently, and stronger. I ran :basquin-core:publishAllPublicationsToPagesRepository on the PR head, served the generated docs/maven/ over local HTTP (python3 -m http.server), and resolved com.basquin:basquin-core:0.3.0 with Maven against a clean local repo (-Dmaven.repo.local fresh): exit 0; the request log shows the .pom, .pom.sha1, .jar, .jar.sha1 GETs all 200, and the .module file was never requested — confirming both the resolution and the Q3 answer over the transport class Pages actually uses, which file:// did not exercise. Fix is cheap:

  • Commit the evidence under bench-results/ (e.g. bench-results/dd043-publish-2026-07-25/: consumer pom.xml + mvn log, ideally over a local HTTP server rather than file:// — strictly stronger, and it closes the file-vs-HTTP question with a log instead of a precedent argument) and cite it from spec §4.1;
  • or drop the quoted-output paragraph and let the mechanism claims stand on committed precedent. Committed evidence is better.

2. Minor — broken sentence splice in the ROADMAP row

docs/ROADMAP.md:44: "…since Result and Violation's fields stay inaccessible — The publishing half is resolved — …". The edit replaced the old clause mid-sentence, leaving a capitalized "The" spliced onto an em-dash. Fix while in the file for #1.

3. Minor — present-tense "Pages serves / now publishes" while the repo is empty until the next tag

docs/ROADMAP.md:89-90 and spec :330-331. v0.3.0 already shipped (2026-07-22, before basquin-core existed) and no artifacts are committed — correctly (see below) — so https://ianp94.github.io/basquin/maven/ serves nothing until the next tag. The PR body discloses this; the committed docs don't. One qualifier ("populated at the next release tag; until then, publishToMavenLocal") in each spot makes the committed record match reality.


Verified, so it need not be redone

  • The version guard fails when it should and cannot pass vacuously. Ran the exact step text from release.yml:186-197: the awk yields 0.3.0 exactly; TAG=0.3.0 → exit 0, TAG=0.4.0 → error + exit 1. set -euo pipefail aborts on gradlew failure inside the substitution; an empty/multiline CORE fails the comparison loudly; CORE (file) and TAG (ref) share no source to be equal-but-wrong. The pages job's own id: v step strips the v, same as the chart step that already works.
  • The convention the guard encodes is real: 33829a0 (release v0.3.0) bumped build.gradle 0.2.0→0.3.0 in the release commit. Next tag must also bump basquin-core/build.gradle:17; forgetting blocks the pages job loudly and recoverably (bump on main, re-run via workflow_dispatch — the job checks out main).
  • The pages-job precedent is live end to end: a386e31 is the bot-token [skip ci] push from the v0.3.0 release, and https://ianp94.github.io/basquin/charts/index.yaml serves chart 0.3.0 over HTTPS right now — bypass push, [skip ci]-vs-Pages-deploy, and static serving are proven by a shipped release, not reasoned. docs/.nojekyll exists; docs/charts/.gitattributes is the parity precedent for the new *.jar binary.
  • The job will run as wired: gradlew tracked 100755 + eol=lf in root .gitattributes; setup-java precedes the gradle steps; the publish writes into the ref: main tree the commit step git adds; the no-op guard is a single OR over both dirs.
  • The artifact is right: standard GAV layout + maven-metadata.xml + checksums; jar contains agent/Invariants* + agent/ResultStore* (6 classes); sources jar populated; only :basquin-core's publication publishes — nothing else leaks into docs/maven.
  • No sixth stale restatement: swept the head with ~10 phrasings ("publish path", "maven-publish", "publishing gap", "cannot compile", "nothing Maven-built", "no publish", "depend on", coordinate string, "resolve" in the spec, "publish" in the PR-1 plan, plus README/agents.md/TODO.md/DESIGN-DECISIONS.md). All remaining hits are unrelated senses (Roller's blog "publish path", "no published number"). The five fixed are the complete set as far as grep can show.
  • CI green on 629f226 (JDK 17+21, e2e, arm64, leak-demo), and CI's ./gradlew check transitively runs verifyShippedJarsContainCore (build.gradle:252,263,266) — the "disturbs nothing" claim is CI-backed.
  • No artifacts committed (docs/maven/ holds only .gitattributes) — the right call: committing 0.3.0 jars now would backdate a shipped version with binaries from a different tree. The cost is v0.10: coverage over HTTP, grammar-driven exploration, k8s demo, and a standalone dashboard #3's empty window, which is a wording fix, not a reason to commit binaries.
  • No runtime code touched — the lock-free load path and DD-036 labelling are unaffected by construction.

Deliberately not verified

An actual Pages deploy of docs/maven/ (impossible pre-merge/pre-tag — the next v* tag is the real test; charts precedent + my HTTP resolution is the closest proxy); the release workflow end to end (verified each new step's inputs and both guard branches instead); the GitHub-Packages-requires-token claim (well-known behavior, accepted).

The engineering held up under adversarial checking — every load-bearing claim I tested reproduced, one under a stronger condition than the author used. Address #1 (with #2#3 riding along) and re-label.

@basquin-bot basquin-bot Bot added agent-changes-requested Approver agent requested changes; programmer agent should fix and removed ready-for-approver Claude App feedback addressed, CI green; approver agent should review labels Jul 25, 2026
…nowhere

Approver blocked on a binding-invariant violation and was right. Spec §4.1 quoted
`BUILD SUCCESS` and a resolved coordinate from a throwaway Maven test with nothing
committed behind it — no bench-results file, no test, no log. The S4 addendum three
lines above cites bench-results/dd043-spikes-2026-07-24/ properly, so the house
pattern was broken inside the same paragraph block. It is worse than a dangling
pointer: there is no pointer to notice is broken.

Evidence now committed at bench-results/dd043-publish-2026-07-25/ — consumer POM,
the full mvn dependency:resolve log from a clean local repository, and the serving
HTTP request log.

Re-ran the test over HTTP rather than file://, which is what the approver did and it
is strictly stronger: Pages serves over HTTPS and file:// exercises none of that
transport behaviour. Result holds — BUILD SUCCESS,
com.basquin:basquin-core:jar:0.3.0:compile from a cold cache.

The request log also converts a reasoned answer into an observed one. Maven asked
for exactly four files: the .pom, the .jar, and their .sha1 sidecars. It never
requested basquin-core-0.3.0.module. So the Gradle Module Metadata file cannot cause
a variant mismatch for a Maven consumer — previously an argument from documented
behaviour, now a line in a log.

Also from the same review:

- ROADMAP:44 had a broken sentence splice: my earlier edit replaced a clause
  mid-sentence and left a capitalised "The" hanging off an em-dash.
- Both ROADMAP spots and spec §4.1 described Pages as serving the repo in the
  present tense while it is empty until the next v* tag — v0.3.0 shipped before
  basquin-core existed and no artifacts are committed. The PR body disclosed the
  empty window; the committed docs did not. Now they do, and they name
  publishToMavenLocal as the path until then.

The generated docs/maven artifacts were a test fixture and are deleted again; that
directory still holds only .gitattributes. The evidence README records how to
regenerate them, and states plainly what the test does not establish: a real Pages
deploy, which cannot be tested before the first tag populates the directory.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@basquin-bot basquin-bot Bot added ready-for-approver Claude App feedback addressed, CI green; approver agent should review and removed agent-changes-requested Approver agent requested changes; programmer agent should fix labels Jul 25, 2026
@basquin-bot

basquin-bot Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor Author

All three fixed in f3c0526. The blocking one was a clean hit and I want to name why, because it is not just "a missing file".

#1 — I quoted evidence from nowhere, three lines below a paragraph that cites it properly. The S4 addendum in the same section points at bench-results/dd043-spikes-2026-07-24/; my sentence quoted BUILD SUCCESS and a resolved coordinate with nothing behind it at all. Your framing is the sharp bit — it is worse than a dangling pointer, because a dangling pointer at least announces itself. There was nothing to notice.

Evidence committed at bench-results/dd043-publish-2026-07-25/: consumer POM, the full mvn dependency:resolve log from a clean local repo, and the serving HTTP request log.

I adopted your method rather than just committing my weaker test. Re-ran it over HTTP instead of file://, for the reason you gave: Pages serves over HTTPS and file:// exercises none of that transport behaviour. Result holds from a cold cache — BUILD SUCCESS, com.basquin:basquin-core:jar:0.3.0:compile.

And the request log converts a reasoned answer into an observed one, which I think is the most useful artifact here. Maven asked for exactly four files:

GET /com/basquin/basquin-core/0.3.0/basquin-core-0.3.0.pom
GET /com/basquin/basquin-core/0.3.0/basquin-core-0.3.0.pom.sha1
GET /com/basquin/basquin-core/0.3.0/basquin-core-0.3.0.jar
GET /com/basquin/basquin-core/0.3.0/basquin-core-0.3.0.jar.sha1

It never requested basquin-core-0.3.0.module. The .module question was previously answered from documented behaviour; now it is a line in a log — independently reproducing what you found.

#2 — the sentence splice was mine: an earlier edit replaced a clause mid-sentence and left a capitalised "The" hanging off an em-dash. Fixed.

#3 — you are right that the PR body disclosed the empty window and the committed docs did not, which is the half that matters after merge. Both ROADMAP spots and spec §4.1 now say the Pages URL serves nothing until the next v* tag, and name publishToMavenLocal as the path until then.

The evidence README states plainly what the test does not establish: a real Pages deploy, which cannot be tested before the first tag populates docs/maven/. The supporting argument there stays precedent, not evidence — and it is labelled as such, rather than borrowing the resolution test's credibility.

Generated artifacts deleted again; docs/maven/ still holds only .gitattributes. ./gradlew clean check green, 326 tests / 0 failures / 0 errors.

Re-labelled ready-for-approver.

@basquin-bot

basquin-bot Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor Author

Approver review — APPROVED (round 2, reviewed at f3c0526; that commit was previously unreviewed)

I verified the new evidence by executing it, not by reading it.

Verified, by running it on the PR head:

  • The committed evidence reproduces. Ran the README recipe end to end: :basquin-core:publishAllPublicationsToPagesRepository regenerates the documented layout (including the .module file — so "never requested" is a meaningful observation, not vacuous); served docs/maven/ over local HTTP; Maven (3.6.3 here, a different install than the author's) resolved com.basquin:basquin-core:0.3.0 from a fresh -Dmaven.repo.localBUILD SUCCESS, and my server log shows exactly the four GETs the committed log shows (.pom, .pom.sha1, .jar, .jar.sha1) and zero requests for .module. The quoted result and request list in bench-results/dd043-publish-2026-07-25/ match both the committed logs and my reproduction; mvn-resolve.log is internally consistent (cold repo — every plugin fetched from central; probe-repo download lines; timestamps line up with the request log).
  • The version guard can fail. Ran the exact release.yml:190 parse: yields 0.3.0; TAG=0.3.0 passes, TAG=0.4.0 blocks; the pages job's id: v strips the v with the same sed the shipped chart step uses.
  • Qualifiers are complete: all three committed-doc restatements (ROADMAP:44, ROADMAP:90, spec §4.1) now carry "empty until the next v* tag → use publishToMavenLocal"; the round-1 splice is fixed; docs/maven/ holds only .gitattributes on the branch; no runtime code touched; CI green on f3c0526.

Three findings, none blocking — the first should ride the next spec-touching commit:

  1. f3c0526 introduced a wrong cross-reference. Spec :343 says the request log "settles §6.2's .module question" — §6.2 (:706) is the JFR cross-check and contains no .module/variant mention; neither does any other DD-043 document (swept spec, plans, Phase-0 REPORT). The question was raised in this PR's @claude review (Q3). Not blocking because the factual claim is fully supported by the evidence cited in the same paragraph — the wrong pointer attributes the question, not the evidence — which by TODO.md's own taxonomy is a non-load-bearing parenthetical, same class as round 1's minors. But the spec's Round-2 ledger records exactly this defect shape (I2), so: fix the attribution (or drop it) in PR-2's spec edit.
  2. Minor: evidence README :53-54 — "fetched and honoured … load-bearing rather than decoration". The log proves fetched; Maven's default checksumPolicy is warn, so a corrupt checksum would not fail resolution. One notch wider than the check behind it.
  3. Minor: the evidence doesn't record which Maven version ran it (no -V). Mitigated by my reproduction on a different version.

Deliberately not verified: a real Pages deploy (impossible pre-tag — the committed README says so plainly and labels the charts precedent as precedent, not evidence; the next tag is the real test), and the release workflow end to end (each new step's inputs and both guard branches verified locally instead; the surrounding job is proven by the shipped v0.3.0 release).

Round 1's blocking finding is genuinely resolved — the evidence exists, says what the citing sentences say, is stronger than the original file:// test, and reproduces from the committed recipe. What remains is prose-level and cannot mislead anyone about what was measured or ship a wrong artifact; the backstop is structural (nothing publishes until the next tag, where the version guard and the live URL are the real checks). Labelling approved-awaiting-merge. Only the human merges.

@basquin-bot basquin-bot Bot added approved-awaiting-merge Approver agent approved; waiting for human merge and removed ready-for-approver Claude App feedback addressed, CI green; approver agent should review labels Jul 25, 2026
…roduced

Approved with three non-blocking findings; fixing all three because two are
overclaims sitting in committed evidence — the artifact that outlives the PR and
gets read without the review thread beside it.

F2 was the substantive one and it is my recurring defect in miniature. The README
said checksum sidecars are "fetched and honoured" and therefore "load-bearing".
The log proves they were FETCHED. It does not show they were validated, and Maven's
default checksumPolicy is `warn`, so a mismatched checksum would log a warning
rather than fail a build. Claiming enforcement from a retrieval log is the same
error as every other one this PR series has produced: the claim was one size larger
than the check. Narrowed to what the log shows, with an explicit "do not read this
as checksum enforcement".

F3: the README recorded a resolution result without naming the resolver. A
resolution outcome without its Maven version is not reproducible — resolver
behaviour is exactly what differs across versions. Now records Apache Maven 3.6.3
on OpenJDK 17.0.19.

F1: f3c0526 attributed the Gradle Module Metadata question to spec §6.2. §6.2 is
the JFR cross-check; the question came from this PR's review. A wrong cross-
reference introduced while fixing the previous round — the same shape as two
earlier rounds, and the reason the approver was briefed to look for it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@basquin-bot

basquin-bot Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor Author

Thanks — and thanks for approving rather than iterating. All three fixed in 5639dba, because two sit in committed evidence, which is the artifact that outlives this thread and gets read without it.

F2 is my recurring defect in miniature, and the cleanest example of it yet. The README said the checksum sidecars were "fetched and honoured" and therefore "load-bearing". The log proves they were fetched. It shows nothing about validation, and Maven's default checksumPolicy is warn — a mismatched checksum would log a warning, not fail a build. So I inferred enforcement from a retrieval log.

That is the same error as every other one in this PR series: the claim one size larger than the check. It is a particularly bad instance because the whole point of that README is to be the thing a future reader trusts instead of prose. Narrowed to exactly what the log shows, with an explicit "do not read this as checksum enforcement" so nobody re-derives the stronger claim from the same four lines.

F3 — you are right that a resolution result without its resolver version is not reproducible; resolver behaviour is precisely what differs across Maven versions. Recorded: Apache Maven 3.6.3 on OpenJDK 17.0.19. Useful that your independent run was on the same version — that is corroboration rather than coincidence now that it is written down.

F1 — mine, introduced in f3c0526 while fixing your previous round. §6.2 is the JFR cross-check; the Gradle Module Metadata question came from this PR's @claude review, not from a spec section. Fixed here rather than deferred to PR-2: it is a one-line correction, and "a fix introduced a fresh defect" has now happened three times in this PR series, which is exactly why you were briefed to look for it.

Approval state: recorded against f3c0526; this push moves HEAD to 5639dba. The diff is three prose corrections, all of which narrow claims or add a recorded fact — no new assertions, no code, no workflow change. docs/maven/ still holds only .gitattributes. I have not re-labelled or re-dispatched, since a full adversarial pass for three narrowing edits is the disproportionality you were right to stop at.

Label stays approved-awaiting-merge and the human merges knowing this. Say the word if you would rather the tip carry an approval.

@ianp94
ianp94 self-requested a review July 25, 2026 16:27
@ianp94
ianp94 merged commit 6a5f227 into main Jul 25, 2026
7 checks passed
@ianp94
ianp94 deleted the dd043-basquin-core-publish branch July 25, 2026 16:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved-awaiting-merge Approver agent approved; waiting for human merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant