Comment fetch: the oldest-first order and the comment-shape probes are not tested for every adapter #783
Labels
No labels
agent-ready
bug
contract
documentation
duplicate
enhancement
good first issue
help wanted
invalid
needs-human
needs-input
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
iterative-works/iw-cli#783
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Split off from the IW-700 analysis (
project-management/issues/IW-700/analysis.md, Defects 1 and 4) by the answer at #700 (comment). IW-700 keeps the GitLab comment-fetch not-found message and the failed-comment-fetch harness tests. Found in IW-610 (review-phase-02-20261003-055441.md, architecture and testing).What is wrong:
IssueFormatter.formatComments(core/output/IssueFormatter.scala:54) prints the headingComments (oldest first):but keeps the order it gets. Only the five adapters sort. The port states the order as one Scaladoc line copied on five methods (core/commands/CommandEnv.scala:403-445), and only GitLab, YouTrack and Linear have an order test at the level of the public fetch function. GitHub and Forgejo test only their parsers.test/contract/gh_adapter_contract.bats:53-82) always skips, because its fixtureiterative-works/iw-cli#1is a merged pull request with no comments, in an archived repository.test/contract/forgejo_api_contract.batshas no comment probe.Fix: state the order for each tracker in the Scaladoc of each
TrackerOps.fetch*IssueComments(the API's own order and its source, and that the adapter returnsIssueComment.oldestFirst); add a fetch-level order test toGitHubClientTestandForgejoClientTest; setGH_FIXTURE_ISSUEto90and make a zero-comment fixture fail, not skip; add a comment-shape probe toforgejo_api_contract.bats. IW-700 writes the not-found meaning into the same Scaladoc; the part that lands second adds to it. Land after IW-672 (PR #735) and IW-749, which change the same lines.Estimate: 1.5-3.5 hours (order contract 1-2h, contract probes 0.5-1.5h).
The analysis text of the two defects follows, as IW-700 wrote it. The reproduction program
Iw700Repro.scalaand the probe scriptprobes.share in the IW-700 analysis under Defect 2; sections D1a and D1b of the program, and the D1 and D4 sections of the script, belong to this part.Probe output for this part (from
probes.sh, exit 0):Defect 1: The "oldest first" heading is not held anywhere above the adapters
Reproduction Steps
Environment: iw-cli worktree
/home/mph/Devel/iw/iw-cli-IW-700at branch IW-700 (HEADd9e76182), Scala 3.3.7, scala-cli. The program runs against the Mill test runtime classpath (core classes plusFakeCommandEnv).Prerequisites:
env -u JDK_JAVA_OPTIONS ./mill show core.test.runClasspathhas been run (it compiles core and the test fixtures).Steps to reproduce:
IssueFormatter.formatCommentstwo comments, newest first.FakeTrackerto return the same list newest first, then runiw issue SAMP-7 --commentsthroughIssue.run.Expected behavior: Under
Comments (oldest first):the older comment comes first.Actual behavior: The newer comment comes first under the "oldest first" heading, in both the formatter and the command.
Reproducibility: Always, for input that is not sorted. Through the real adapters: never today, because all five sort (see Hypothesis 2).
The reproduction program
Iw700Repro.scalais in the IW-700 analysis under Defect 2. Output of its D1 sections (exit 0):Root Cause Hypotheses
Hypothesis 1: The order is an adapter duty that nothing above the adapters holds ⭐ Most Likely
Evidence:
CommandEnv.scala:403-445, "Every comment of one issue, oldest first"). The line does not say what each tracker API returns or how the adapter enforces the order.formatCommentsrenders "in the order given" (IssueFormatter.scala:49-50).IssueFormatterTest"formatComments keeps the given order" pins on purpose that it does not sort (IW-610phase-02-context.md:337).Likelihood: High (confirmed by the repro and by reading the code).
Investigation approach:
IssueComment.oldestFirstis used in all five adapters:LinearClient.scala:110,YouTrackClient.scala:71,GitHubClient.scala:414,ForgejoClient.scala:181,GitLabClient.scala:327.GitLabClientTest:1199parse,:1296fetch), YouTrack (YouTrackClientTest:345fetch), Linear (LinearClientMockTest:570fetch across cursor pages), GitHub (GitHubClientTest:1132parse only), Forgejo (ForgejoClientTest:396parse only).Fix strategy:
phase-01-context.md:117,phase-02-context.md:273), and the IW-700 Fix line ("make the comments contract explicit per adapter") agrees. A recorded decision settles this, so this analysis has no CLARIFY marker here.formatComments. That reverses the recorded decision and adds a second place that owns the order.TrackerOps.fetch*IssueComments. Replace the copied line with Scaladoc for that tracker. It states (a) the API's own order and its source, (b) that the adapter returnsIssueComment.oldestFirst(ties keep the API order), and (c) what a not-foundLeftmeans (IW-700 Defect 2 writes that part). The per-tracker order facts:gh issue view --json commentsreturned ascendingcreatedAtoncli/cli#13840(149 comments) anditerative-works/iw-cli#90.sort=desc,order_by=created_at(docs.gitlab.com/api/notes, "List project issue notes"). The adapter asks forsort=asc&order_by=created_at.createdAtfield" (linear.app/developers/pagination). The direction is not documented.resource-api-issues-issueID-comments). Onlyfields,$skipand$topexist.sinceandbefore(code.iterative.worksswagger.v1.json). Issue 636 returned 6 comments in ascending order.GitHubClientTestandForgejoClientTest, with the same name in all five suites: "fetchIssueComments returns comments oldest first when the API answers newest first". Then each adapter's public fetch function, not only its parser, holds the contract.formatCommentsScaladoc to say that the caller passes comments oldest first, asTrackerOpspromises. Keep the "keeps the given order" test, because it pins the decision.Risk of fix: Low. The change is documentation plus two tests. Production code does not change.
Hypothesis 2: An adapter returns comments that are not sorted today
Evidence against: All five adapters call
IssueComment.oldestFirst(see the line references above), and each suite has an order test.Likelihood: Low (ruled out by reading the code).
Investigation approach:
Fix strategy: None.
Risk of fix: Not applicable.
Hypothesis 3: YouTrack paging relies on an order that is not documented
Evidence:
YouTrackClient.fetchIssueCommentspages with$skip, and the YouTrack comments endpoint documents no ordering parameter and no guaranteed order. If the server order is not stable between page requests, a page boundary could drop or repeat a comment.oldestFirstcannot fix that.Likelihood: Low (nobody has observed it, and no YouTrack instance was available to probe).
Investigation approach:
$top=100&$skip=0and$skip=100twice, and compare the ids.Fix strategy: Out of scope for this issue. The Defect 1 Scaladoc for YouTrack names this assumption. Record it as one line in
FOLLOW-UPS.md.Risk of fix: Not applicable.
Defect 4: The gh contract probe for the comment shape never runs
Reproduction Steps
Environment: gh authenticated to github.com (this machine: account
mprihoda), bats.Steps to reproduce:
cd /home/mph/Devel/iw/iw-cli-IW-700 && bats --tap -f 'issue view --json returns' test/contract/gh_adapter_contract.batsprobes.sh(section D4 above).Expected behavior: The probe checks
author.login,createdAtandbodyof a real comment.Actual behavior: Bats output (exit 0):
The skip comes from
gh_adapter_contract.bats:75-77. The probe shows that the fixtureiterative-works/iw-cli#1is a merged pull request with"comments":[], in a repository with"isArchived":true.Reproducibility: Always.
Root Cause Hypotheses
Hypothesis 1: The fixture has no comments and cannot get any ⭐ Most Likely
Evidence:
#1has stateMERGED,comments: []. The repository is archived.iterative-works/iw-cli#90is an issue (stateCLOSED) with 2 comments, each with a stringauthor.login,createdAtandbody(probe). Because the repository is archived, the fixture cannot change.Likelihood: High (confirmed).
Investigation approach:
Fix strategy:
GH_FIXTURE_ISSUEto90. One fixture serves both auth-gatedissue viewprobes. A real issue is a better fixture forgh issue viewthan a pull request. Write in the comment that the mirror is archived, so the fixture cannot change.:75-77with a failure. A frozen fixture with zero comments means that gh changed its output or that the fixture is gone. A silent skip is the defect this issue reports.test/contract/forgejo_api_contract.bats. It runs anonymously against the publiciterative-works/iw-cliissue 636 (6 comments; the fieldsuser.login,created_atandbodyare whatForgejoClient.parseIssueCommentsResponsereads). With it, every adapter that has a contract file has a comment probe. GitLab already has one (glab_adapter_contract.bats:87-114). Linear and YouTrack have no contract file.Risk of fix: Low. Contract tests run only nightly and on
contract-labelled pull requests.Hypothesis 2: In CI, the probe skips before it reaches the fixture
Evidence: The log of the nightly contract job (run on
33042e12, job 4040, 2026-10-07 03:21 UTC, read through the Forgejo Actions API) shows:ci.yml:242-244setsGH_TOKEN: ${{ secrets.GITHUB_TOKEN }}. On Forgejo Actions that token comes from Forgejo, sogh auth status --hostname github.comfails. That run came before the IW-610 merge, but the auth mechanism has not changed since.Likelihood: High (confirmed from the CI log).
Investigation approach:
Fix strategy: Outside the issue text. A real github.com token in CI is a secret that a human must provision. Record it as one line in
FOLLOW-UPS.md. Theci.ymlcomment "so the JSON-shape tests don't skip on the nightly run" is false on this forge. Until that follow-up is done, the Defect 4 fix runs on authenticated developer machines and oncontract-labelled pull requests only.Risk of fix: Not applicable.
Split off from #700.
Intake: agent-ready
Workflow: diagnostic
Reason: all gates passed
Analysis: project-management/issues/IW-783/analysis.md on branch IW-783
Implement: needs-human
Reason: batch-implement exit 1 — the kanon plugin does not compile against the iw-cli core on current main
kanon/lib/batch/ForgeCatchUp.scala:143expectsEither[String, Option[ForgeState]]fromtracker.fetchPullRequestForBranch. Since #778 ("TrackerError on the forge clients and the port's PR/CI/merge calls"), the port returnsEither[TrackerError, …]. No phase ran. The branch holds only the issue folder, merged with main at33e14e5f. Every iw-cli issue fails the same way until the plugin is updated.Release: the implement loop failed this issue for a reason outside it. The kanon plugin did not compile against iw-cli main, because the tracker port now returns TrackerError (iw-cli #778). The plugin fix is dev-docs #484, and iw-cli 0.22.1 carries the core change. The issue goes back to the implement loop.