Skip to content

Make /progress effort-agnostic and add --full - #970

Merged
thomasluizon merged 11 commits into
redesign/mainfrom
chore/progress-generic-full
Sep 17, 2026
Merged

thomasluizon merged 11 commits into
redesign/mainfrom
chore/progress-generic-full

Conversation

@thomasluizon

@thomasluizon thomasluizon commented Sep 15, 2026 •

Copy link
Copy Markdown
Owner

Closes thomasluizon/orbit-tickets#558

Summary

/progress now identifies one integration branch, reports session work only from schema-backed ownership, and proves arrival by commit ancestry.

  • It starts from the checkout branch's exact open pull request head.
  • It follows stacked bases until the candidate is not another open head.
  • It uses the checkout branch when that branch has no open pull request.
  • It enumerates session pull requests from readinessLedger only when sessionId matches currentRunIdentifier().
  • It treats MERGED as an intermediate fact and verifies the merge commit is an ancestor of the integration branch.
  • It reports the effort scope instead when no session baseline exists.
  • It states that uncommitted work in linked worktrees is invisible from the orchestrating checkout.
  • It never reads the undeclared run-state merged key.

Branch and session evidence

Chosen integration rule: begin with the pull request whose headRefName equals this checkout branch. Follow each baseRefName while it is another open headRefName. The first base that is not an open head is the integration branch. This cannot tie on unrelated bases because traversal begins from one exact head. A duplicate matching head or cycle is reported as ambiguous.

Exact real invocation:

git rev-parse --abbrev-ref HEAD

Complete response: stdout was chore/progress-generic-full, stderr was empty, and the exit code was 0.

Exact real invocation:

gh pr list --repo thomasluizon/orbit-ui-mobile --state open --limit 100 --json number,headRefName,baseRefName

Complete selected response key shape: Array<{baseRefName:string, headRefName:string, number:number}>.

[{"baseRefName":"redesign/main","headRefName":"feature/ticket-71-perfil-s10","number":983},{"baseRefName":"redesign/main","headRefName":"feature/ticket-67-onboarding-s1","number":981},{"baseRefName":"redesign/main","headRefName":"chore/progress-generic-full","number":970},{"baseRefName":"main","headRefName":"dependabot/github_actions/github-actions-ac39c799b5","number":881},{"baseRefName":"main","headRefName":"dependabot/npm_and_yarn/fast-uri-3.1.7","number":801},{"baseRefName":"main","headRefName":"dependabot/npm_and_yarn/xmldom/xmldom-0.8.15","number":799},{"baseRefName":"main","headRefName":"dependabot/npm_and_yarn/orval-8.27.0","number":798}]

Session ownership reads sessionId:string, remaining:Array<string>, and readinessLedger. Each ledger row reads {repositoryKey:string, prNumber:number, receiptPath:string}. The receipt path supplies provenance, not merge status.

The replacement for the undeclared merged field reads live state and nullable mergeCommit.oid for each ledger pull request. It then tests the merge commit against origin/<integration-branch>. A stacked child cannot pass falsely because its merge commit remains only on its parent branch until that parent reaches the integration branch.

With no matching run record, there is no session-start baseline. The skill now says so and reports effort scope instead. The orchestrating checkout also cannot see uncommitted pre-pull-request changes in linked worktrees, so the skill states that limitation.

Observed command contracts

Exact real invocation:

git fetch origin redesign/main

Complete response, with exit code 0:

From https://github.com/thomasluizon/orbit-ui-mobile
 * branch              redesign/main -> FETCH_HEAD

Exact real invocation:

git status --short

Complete response: stdout and stderr were both zero bytes, and the exit code was 0. The empty response therefore represented a clean checkout in this observed run.

Exact real invocation:

gh pr view 963 --repo thomasluizon/orbit-ui-mobile --json state,mergeCommit

Complete selected response and types: {"mergeCommit":{"oid":"<40-character commit oid>"},"state":"MERGED"}, shaped as {mergeCommit:{oid:string}|null,state:string}.

Exact real invocation:

gh pr view 970 --repo thomasluizon/orbit-ui-mobile --json state,mergeCommit

Complete selected response: {"mergeCommit":null,"state":"OPEN"}. The previously observed complete PullRequestState set is OPEN, CLOSED, and MERGED.

Exact real invocations:

git merge-base --is-ancestor 8972d5af0291530d6b2d2c71b52ab62042e2fec1 origin/redesign/main
git merge-base --is-ancestor 673918fc699a32fe5f5f384c4626d5658ee7457c origin/redesign/main

Both commands returned empty stdout and stderr. The known integrated merge commit exited 0. The branch-only commit exited 1.

Assumptions

  • Regression assertions belong in .claude/hooks/test-hooks.mjs; I rejected a standalone harness because this file already scans every skill contract.

Test evidence

  • Before changes, unchanged node .claude/hooks/test-hooks.mjs passed with ORBIT HOOKS OK while both round-two defects were present.
  • With the defects still present, $hookOutput = & node .claude/hooks/test-hooks.mjs; $hookExit = $LASTEXITCODE; $hookOutput | Select-String -Pattern 'progress:|ORBIT HOOKS|FAIL'; exit $hookExit failed the new ancestry and missing-baseline assertions and ended with ORBIT HOOKS FAILED (2).
  • After the fix, the same focused command passed all three progress assertions.
  • Final node .claude/hooks/test-hooks.mjs passed with ORBIT HOOKS OK.
  • node tools/test-tools.mjs passed with ORBIT TOOLS GATE OK.
  • node tools/reseed-calibration.mjs stamped 31 files and renewed only the progress verdict.
  • node tools/check-calibration.mjs passed with 31 calibrated files.
  • git diff --check passed.
  • Pre-commit dashes, i18n usage, and root allowlist gates passed.

Round 3 delivery: the worker was killed before it could push

The worker that wrote 81586aa3 was killed by the harness after it committed and before it pushed or
reported. It did not fail. Its tree was clean and its commit was complete.

The orchestrator verified that commit and pushed it. None of the worker's own numbers are quoted
for this commit.
Everything under the next heading is a run the orchestrator performed itself, from
the clean tree at 81586aa3, in
C:\Users\thoma\orca\workspaces\orbit-ui-mobile\ticket-541-progress-generic.

Note that the Test evidence section above belongs to the EARLIER rounds. It reports 31 calibrated
files; 81586aa3 reseeds again and the current figure is 32.

Test evidence for 81586aa3, produced by the orchestrator

Green. node .claude/hooks/test-hooks.mjs exits 0 and ends ORBIT HOOKS OK, with:

PASS progress: a missing session baseline degrades explicitly to effort scope
PASS progress: each ledger repository resolves and refreshes its own integration branch
PASS progress: stacked squash merges do not make an impossible ancestry promise

Red first, proved after the fact rather than claimed. A red-first run that happened only inside a
session that no longer exists is not evidence, so the failure was reproduced on the commit itself.
Reverting ONLY the source file the new assertions read, and nothing else:

git checkout 81586aa3~1 -- .claude/skills/progress/SKILL.md
node .claude/hooks/test-hooks.mjs

exits 1 and fails exactly the two assertions this commit adds, and no others:

FAIL progress: each ledger repository resolves and refreshes its own integration branch
  got={"readsLiveBase":false,"anchorsResolutionToSessionPullRequest":false,
       "resolvesInsideMappedRepository":false,"scopesFetchFailure":false}
FAIL progress: stacked squash merges do not make an impossible ancestry promise
  got={"limitsAncestryToDirectIntegrationMerges":false,"reportsImmediateBase":false,
       "admitsSquashBoundary":false,"namesRetargetPractice":false}
ORBIT HOOKS FAILED (2)

git checkout 81586aa3 -- .claude/skills/progress/SKILL.md then restores the tree, and
git status --porcelain is empty.

The rest, all from the restored clean tree:

  • node tools/test-tools.mjs exits 0, zero FAIL lines.
  • node tools/check-calibration.mjs exits 0: "32 calibrated file(s) stamped 2026-09-16 against codex
    tiers default, mechanical, oldest verdict 2 day(s) old."

No root type-check or lint run is quoted for this commit. This pull request changes four files,
all of them harness documentation and harness tests, and no application source. CI runs both gates on
this head and they are the evidence for them.

External interface: gh pr view --json state,mergeCommit,baseRefName

SKILL.md:68 prescribes gh pr view <number> --json state,mergeCommit,baseRefName. The previous
revision of this section proved only that baseRefName is an ACCEPTED field, from the no-value
--json listing, and that listing was elided and carried no types. Both gaps are closed here with
the real three-field invocation and its complete selected key and type shape, measured 2026-09-17
against gh version 2.97.0 (2026-07-31).

MERGED, the state whose mergeCommit.oid the skill feeds to git merge-base --is-ancestor:

$ gh pr view 986 --repo thomasluizon/orbit-ui-mobile --json state,mergeCommit,baseRefName
{"baseRefName":"redesign/main","mergeCommit":{"oid":"43bc28ad2b203a64649c4eb6d1f35d64ccb5c965"},"state":"MERGED"}

Its complete selected key set with each value's JSON type, from
--jq 'to_entries|map({key:.key, type:(.value|type)})':

[{"key":"baseRefName","type":"string"},{"key":"mergeCommit","type":"object"},{"key":"state","type":"string"}]

mergeCommit is an object, and its own complete key set, from
--json mergeCommit --jq '.mergeCommit|to_entries|map({key:.key, type:(.value|type)})', is one
string field:

[{"key":"oid","type":"string"}]

OPEN and CLOSED, the two states the skill reports without an ancestry check. Both carry
mergeCommit: null, which is why the skill requires a non-null mergeCommit.oid before it runs
merge-base:

$ gh pr view 991 --repo thomasluizon/orbit-ui-mobile --json state,mergeCommit,baseRefName
{"baseRefName":"redesign/main","mergeCommit":null,"state":"OPEN"}

$ gh pr view 560 --repo thomasluizon/orbit-ui-mobile --json state,mergeCommit,baseRefName
{"baseRefName":"main","mergeCommit":null,"state":"CLOSED"}

So state is a string over OPEN, CLOSED and MERGED; baseRefName is a string in every state;
mergeCommit is either null or an object with a single string oid. That is the whole shape the
skill consumes.

A closed stacked parent is not the integration branch

The base-chain walk searched only OPEN pull request heads and then accepted every absent head as the
integration branch. A stacked parent can be CLOSED while it is still the child's recorded base, and
this repository contains a live example, read on 2026-09-17:

$ gh pr list --repo thomasluizon/orbit-ui-mobile --head feature/539-b5-apply-design --state all \
    --json number,state,baseRefName
560 CLOSED base=main

$ gh pr view 575 --repo thomasluizon/orbit-ui-mobile --json number,state,headRefName,baseRefName
575: state=CLOSED head=feature/calendar-sync-redesign base=feature/539-b5-apply-design

PR 575's base is feature/539-b5-apply-design, that branch is PR 560's head, and 560 is CLOSED with
mergeCommit null. The old walk stopped there and reported a feature branch as the integration
branch for that whole chain.

SKILL.md step 3 now resolves every candidate in every state with
gh pr list --head <candidate> --state all --limit 10 --json number,state,baseRefName, takes the
highest number because a branch name can be reused, follows an OPEN or MERGED parent to its
base, and ends the walk on a CLOSED unmerged parent by naming that pull request instead of
reporting its head as integration. The first candidate that is no pull request's head, in any state,
is the integration branch.

--head and --state all are not assumed: the invocation above is a real run of the installed
binary and its typed output is quoted.

Test evidence for c0c86de9

Red first, in .claude/hooks/test-hooks.mjs. The new case
progress: a closed stacked parent is never reported as the integration branch was added before the
SKILL.md change and node .claude/hooks/test-hooks.mjs exited 1 with exactly one failure:

FAIL progress: a closed stacked parent is never reported as the integration branch
  got={"resolvesEveryState":false,"followsOpenAndMergedParents":false,"refusesClosedParent":false,"requiresNoHeadInAnyState":false}
  want={"resolvesEveryState":true,"followsOpenAndMergedParents":true,"refusesClosedParent":true,"requiresNoHeadInAnyState":true}
ORBIT HOOKS FAILED (1)

Green after the change: node .claude/hooks/test-hooks.mjs exited 0 with ORBIT HOOKS OK and zero
FAIL lines.

node tools/test-tools.mjs exited 0 with ORBIT TOOLS GATE OK. Its output contains two lines
matching FAIL, and both are PASS lines whose test NAME contains the word, verify-delivery.mjs: a red required check is CI_FAILING, never DELIVERED and verify-delivery.mjs: CI_FAILING names the checks, so the report never says merely that something is red. Zero real failures. The complete run
was captured to a file before the result was quoted.

.claude/skills/progress/SKILL.md is a calibrated file, so its digest changed and
node tools/check-calibration.mjs failed. Its verdict was reconsidered rather than rubber-stamped,
and stays at medium effort with one more judgement named in the reason: the walk now has to classify
a parent's state rather than look one up. After node tools/reseed-calibration.mjs,
check-calibration exited 0: 32 calibrated file(s) stamped 2026-09-17 against codex tiers default, mechanical, oldest verdict 3 day(s) old, with 1 verdict renewed and 31 carried forward.

This round was written by the orchestrator under the step 8 review-fix authority, not by a worker.

Round 4: the all-state listing's real contract, and one rule for a reused head

Head 98c61a64.

The evidence for gh pr list --head <candidate> --state all, corrected

The previous round's section was wrong about this and the review was right. It quoted
560 CLOSED base=main, which is --jq-formatted output, under a --json invocation that emits
JSON. That is a rendering, not a response. Here is the exact command this skill runs, unaltered,
measured 2026-09-17 against gh version 2.97.0 (2026-07-31):

$ gh pr list --repo thomasluizon/orbit-ui-mobile --head feature/539-b5-apply-design \
    --state all --limit 10 --json number,state,baseRefName
[{"baseRefName":"main","number":560,"state":"CLOSED"}]

The top level is an array, from --jq 'type':

array

Each row's complete key set with types, from
--jq '[.[]|(to_entries|map(.key+":"+(.value|type))|join(", "))]':

["baseRefName:string, number:number, state:string"]

So baseRefName is a string, number is a number, and state is a string.

The terminator has its own shape and it is now load-bearing. A head no pull request owns returns
an empty array, which is what ends the walk:

$ gh pr list --repo thomasluizon/orbit-ui-mobile --head redesign/main --state all --limit 10 \
    --json number,state,baseRefName
[]

Every state value this listing can produce, counted across all 835 pull requests in the repository
with --state all --limit 1000 --json state --jq '[.[].state]|group_by(.)|map({value:.[0],count:length})':

[{"count":46,"value":"CLOSED"},{"count":780,"value":"MERGED"},{"count":9,"value":"OPEN"}]

Three values, all three present, and the skill branches on exactly those three.

One deterministic rule for a reused head

Step 3 said both "take the highest number" and "a duplicate matching head or a cycle is
ambiguity". Those are two answers to one response, so the same listing could either continue the
walk or refuse it. A reused branch name makes several rows NORMAL, so the ambiguity line was the
wrong half:

  • Exactly one row is OPEN: that row decides, whatever the numbers are, because a branch can have
    only one live pull request.
  • No row is OPEN: the highest number decides, because numbers increase.
  • Two or more rows are OPEN: that is the real ambiguity, it should not happen, and it refuses by
    name.
  • A cycle still refuses.

Test evidence for 98c61a64

Red first. Two new .claude/hooks/test-hooks.mjs cases were written against the old SKILL.md,
with the file stashed so only the skill reverted:

FAIL progress: a closed stacked parent is never reported as the integration branch
  got={... "terminatesOnAnEmptyArray":false}
FAIL progress: a reused head has exactly one deterministic rule
  got={"oneOpenRowDecides":false,"highestNumberWhenNoneOpen":false,"twoOpenRowsAreTheAmbiguity":false,"noContradictoryDuplicateRule":true}
ORBIT HOOKS FAILED (2)

noContradictoryDuplicateRule is the one that asserts the old sentence is GONE, which is why it is
the only key already true.

Green after: node .claude/hooks/test-hooks.mjs exits 0 with ORBIT HOOKS OK, and
node tools/test-tools.mjs exits 0 with ORBIT TOOLS GATE OK and zero real failures.

node tools/reseed-calibration.mjs renewed 1 verdict and carried 31 forward;
check-calibration then exits 0 at 32 calibrated file(s) stamped 2026-09-17.

One local repair worth recording, because it is not a code change and it cost a round.
tools/test-tools.mjs first died on ERR_MODULE_NOT_FOUND for sharp, then for fast-check.
Neither package was missing: this worktree's tools/node_modules is a JUNCTION to the main
checkout's node_modules, and that directory held 956 empty package directories left behind by
an npm install the machine killed for low memory. Node resolved the empty husk first and stopped.
Removing the husks with rmdir, which cannot touch a populated directory, let resolution fall
through to the worktree's own healthy node_modules and the harness ran clean. No repository file
was involved.

External interface evidence

Measured with:

gh version 2.97.0 (2026-07-31)

gh pr list --json with no value makes the installed binary print every field it accepts.
isCrossRepository is on that list, alongside headRepository and headRepositoryOwner.

Real invocation:

gh pr list --repo thomasluizon/orbit-ui-mobile --state open --limit 3 \
  --json number,headRefName,baseRefName,isCrossRepository

Unaltered response:

[{"baseRefName":"chore/ticket-560-sweep-order","headRefName":"fix/ticket-570-harness-gate","isCrossRepository":false,"number":1010},
 {"baseRefName":"redesign/main","headRefName":"feature/ticket-67-onboarding","isCrossRepository":false,"number":1007},
 {"baseRefName":"redesign/main","headRefName":"feature/ticket-73-static","isCrossRepository":false,"number":1005}]

Complete selected key and type shape, taken from that same response:

baseRefName: string, headRefName: string, isCrossRepository: boolean, number: number

Round 12 executable contract

tools/lib/integration-branch.mjs now executes the base-chain rule. The walk is capped at 100
candidates, far beyond a practical pull request stack, so an invalid lookup cannot return unique
branches forever. Explicit cycle detection stops shorter loops.

Red before the fix, after the fork-only case existed:

FAIL integration-branch.mjs: a fork-only checkout starts from the checkout branch
     resolved someone-elses-branch instead of feature/x

Green after the fix:

PASS fork-only checkout resolved feature/x

Final evidence:

  • node tools/test-tools.mjs exited 0 with ORBIT TOOLS GATE OK and eight integration-branch assertions.
  • node .claude/hooks/test-hooks.mjs exited 0 with ORBIT HOOKS OK.
  • node tools/check-calibration.mjs exited 0 with 32 calibrated files.
  • git diff --check exited 0.

/progress carried the redesign inside it: a hardcoded redesign/main, a
table of seven redesign ticket numbers, and a description that asked
"where is the redesign". It is a generic progress skill.

It now resolves its own working branch from git and its integration
branch from the open pull requests, finds the effort's spec under
.claude/specs/ the way /handoff does, and derives what each ticket means
to a person from that ticket at answer time. The ticket table is deleted
rather than generalised: a table of numbers rots the moment a ticket is
retitled, split or closed.

The default scope moves to THIS SESSION, because mid-run he is asking
what just happened and a whole-effort summary buries it. --full answers
the session first and then the whole spec underneath, labelled per scope,
and earns the extra room the writing contract normally refuses.

Also reseeds two calibration entries that had drifted before this change:
handoff/SKILL.md, last moved by a direct commit that ran no PR checks,
and orchestrate/SKILL.md from #890. Both verdicts were reread and still
describe their files, so they carry forward with new digests.

Harnesses: node tools/test-tools.mjs and node .claude/hooks/test-hooks.mjs
both pass. node tools/check-calibration.mjs reports 31 files stamped.

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

vercel Bot commented Sep 15, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
orbit-ui-mobile-web Ignored Ignored Sep 17, 2026 8:02pm UTC

Request Review

@pullfrog pullfrog 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.

Important

The new live-state procedure cannot yet identify one integration branch or enumerate all work in the current session, so /progress can return incomplete or incorrect answers.

Reviewed changes Reviewed the one-commit rewrite of /progress and its calibration updates.

  • Scope model: The default report now covers the current session, while --full appends whole-effort state and an optional area narrows both scopes.
  • Live-state discovery: Redesign-specific branches and ticket meanings are replaced with Git, GitHub, run-state, effort-spec, and ticket lookups.
  • Calibration: The progress verdict and related drifted digests are refreshed after the skill edit.

ℹ️ The cited test cannot exercise --full

The PR body says the skill commands hook would catch an invalid /progress --full argument, but the hook only scans lines beginning with node tools/*.mjs; this invocation never enters that scan. Its passing result therefore provides no coverage for the new argument behavior.

Technical details
# Replace the inapplicable test evidence

## Affected sites
- `.claude/hooks/test-hooks.mjs:1131` - the regex accepts only `node tools/<tool>.mjs ...` command lines.
- PR body, `Test evidence` - attributes `/progress --full` coverage to that unrelated scan.

## Required outcome
- Exercise the new argument contract with a check that can fail when it regresses, or accurately state that this behavior was validated manually and remains outside automated coverage.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using GPT Sol | 𝕏

Comment thread .claude/skills/progress/SKILL.md Outdated
Comment thread .claude/skills/progress/SKILL.md Outdated

@pullfrog pullfrog 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.

Important

The revised evidence model can still misstate what this session changed and what reached the integration branch.

Reviewed changes Reviewed the evidence and regression updates added since the prior Pullfrog review.

  • Integration resolution: Anchored branch discovery to the checkout's exact open PR head, followed stacked bases, and fetched the resolved branch.
  • Session evidence: Replaced the undeclared merged field with a matching readinessLedger, live PR states, and a current-checkout status read.
  • Contract coverage: Added progress-skill assertions to the hook harness and refreshed the progress calibration.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using GPT Sol | 𝕏

Comment thread .claude/skills/progress/SKILL.md
Comment thread .claude/skills/progress/SKILL.md Outdated
…ss-generic-full

# Conflicts:
#	.claude/calibration.json
@thomasluizon

Copy link
Copy Markdown
Owner Author

@pullfrog review

@pullfrog pullfrog 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.

Important

The current head only merges the updated base, so the two open correctness threads from the prior Pullfrog review still apply; the external CLI evidence is also incomplete.

Reviewed changes Reviewed the complete four-file PR at 673918fc, including the new base merge and the unchanged /progress rewrite.

  • Scope model: /progress defaults to session work, while --full appends effort-wide state and an optional area narrows both scopes.
  • Evidence discovery: The skill resolves an integration branch, reads session-owned pull requests from run state, queries live pull request state, and inspects the current checkout.
  • Contract guards and calibration: The hook harness adds text-level progress assertions, and the calibration digest and verdict are refreshed.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using GPT Sol | 𝕏

Comment thread .claude/skills/progress/SKILL.md

@pullfrog pullfrog 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.

Important

The new ancestry proof still cannot reliably identify landed session work across sibling repositories or stacked squash merges.

Reviewed changes Reviewed the ancestry and missing-baseline fixes added since the prior Pullfrog review at 673918fc.

  • Integration ancestry: Added live mergeCommit.oid reads and git merge-base --is-ancestor checks before calling merged pull requests landed.
  • Missing baseline: Replaced the checkout fallback with an explicit no-baseline result and one effort-scope report while documenting the linked-worktree blind spot.
  • Contract guards: Added textual assertions for ancestry and baseline behavior and refreshed the progress calibration digest.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using GPT Sol | 𝕏

Comment thread .claude/skills/progress/SKILL.md Outdated
Comment thread .claude/skills/progress/SKILL.md Outdated

@pullfrog pullfrog 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.

Important

The new per-repository resolver depends on an external response field whose exact command shape is not yet evidenced.

Reviewed changes Reviewed the per-repository integration and squash-boundary fixes added since the prior Pullfrog review.

  • Repository-specific resolution: Anchored each ledger pull request to its own live base, resolved the integration branch inside that mapped repository, and scoped fetch failures per repository.
  • Squash-boundary handling: Limited ancestry proof to pull requests that directly target integration and made other merged bases explicitly unverifiable.
  • Regression contract: Added hook assertions for both fixes and refreshed the progress calibration verdict.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using GPT Sol | 𝕏

Comment thread .claude/skills/progress/SKILL.md
@thomasluizon

Copy link
Copy Markdown
Owner Author

@pullfrog review

@pullfrog pullfrog 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.

Important

The three-field gh pr view response that now drives per-repository branch resolution is still not evidenced in the live PR body.

Reviewed changes Reviewed the complete current head and rechecked the external-interface evidence after the latest thread was resolved.

  • Scope model: /progress now defaults to current-session behavior, while --full appends effort-wide state and an optional area narrows both scopes.
  • State evidence: The skill resolves integration branches per repository, enumerates session-owned pull requests from readinessLedger, and distinguishes direct ancestry from unprovable stacked squash boundaries.
  • Regression contract: The hook harness adds source-level checks for the new progress contract, while calibration metadata is refreshed for the rewritten skill.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using GPT Sol | 𝕏

Comment thread .claude/skills/progress/SKILL.md
@thomasluizon

Copy link
Copy Markdown
Owner Author

@pullfrog review

@pullfrog pullfrog 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.

Important

The integration walk still confuses closed stacked heads with integration branches, and the new external-interface evidence remains incomplete.

Reviewed changes Reviewed the complete six-commit rewrite of /progress, including its current PR-body evidence.

  • Scope model: /progress now defaults to current-session behavior, while --full appends effort-wide state and an optional area narrows both scopes.
  • State discovery: The skill resolves branches per repository, reads session-owned pull requests from readinessLedger, and distinguishes direct ancestry from stacked squash boundaries.
  • Regression contract: The hook harness adds source-level checks for the progress procedure, while calibration metadata is refreshed for the rewritten skill.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using GPT Sol | 𝕏

Comment thread .claude/skills/progress/SKILL.md Outdated
Comment thread .claude/skills/progress/SKILL.md
The base-chain walk searched only open pull request heads, then accepted every
absent head as the integration branch. A stacked parent can be CLOSED while it
is still the child's recorded base: PR 575's base is
`feature/539-b5-apply-design`, that branch is PR 560's head, and 560 is CLOSED
with `mergeCommit` null. The walk therefore named a feature branch as the
integration branch for that whole chain.

Each candidate now resolves in every state through
`gh pr list --head <candidate> --state all`. An OPEN or MERGED parent is
followed to its base; a CLOSED unmerged parent ends the walk as an unresolved
chain that names the parent instead of reporting its head as integration.

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

@pullfrog pullfrog 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.

Important

The all-state walk has conflicting branch-reuse instructions and lacks valid evidence for its new CLI response; both must be resolved before merge.

Reviewed changes Reviewed the closed-parent fix added since the prior Pullfrog review at 81586aa3.

  • All-state parent lookup: Extended base-chain traversal from open pull request heads to OPEN, MERGED, and CLOSED records.
  • Closed-chain handling: Stopped a closed, unmerged stacked parent from being reported as the integration branch.
  • Regression contract: Added a hook assertion for closed-parent handling and refreshed the progress calibration verdict.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using GPT Sol | 𝕏

Comment thread .claude/skills/progress/SKILL.md Outdated
Comment thread .claude/skills/progress/SKILL.md Outdated
@thomasluizon

Copy link
Copy Markdown
Owner Author

@pullfrog review

@pullfrog pullfrog 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.

Important

The merge from redesign/main does not address the two open correctness threads: reused heads still have conflicting outcomes, and the all-state gh pr list response remains unevidenced.

Reviewed changes Reviewed the complete current head at 196e503c, including the base merge after the prior Pullfrog review.

  • Scope model: /progress defaults to current-session behavior, while --full appends effort-wide state and an optional area narrows both scopes.
  • State discovery: The skill resolves integration branches per repository, reads session-owned pull requests from readinessLedger, and distinguishes direct ancestry from stacked squash boundaries.
  • Closed-parent handling: The branch walk queries all pull request states and refuses to identify a closed, unmerged stacked parent as integration.
  • Regression contract: The hook harness adds source-level checks for the progress procedure, while calibration metadata is refreshed for the rewritten skill.

Pullfrog  | Fix it ➔ | View workflow run | Using GPT Sol | 𝕏

Step 3 gave two incompatible decisions for the same multi-row result: take
the highest number, and treat a duplicate matching head as ambiguity. A
branch name can be reused, so `/progress` could either continue the walk or
refuse it depending on which sentence it read.

A reused head is normal, not ambiguous, and now has one rule. Exactly one
OPEN row decides whatever the numbers are, because a branch can have only
one live pull request. With no OPEN row the highest number decides, because
numbers increase. Two OPEN rows sharing a head is the real ambiguity and
still refuses. A cycle still refuses.

An empty array is what ends the walk: the candidate is no pull request's
head, so it is the integration branch.

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

@pullfrog pullfrog 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.

Important

The reused-head rule can select an unrelated fork pull request with the same branch name and resolve the wrong integration branch.

Reviewed changes Reviewed the deterministic reused-head fix and corrected gh pr list contract evidence added since the prior Pullfrog review at 196e503c.

  • Head selection: Defined a sole-open, otherwise highest-numbered rule while retaining explicit ambiguity for multiple open rows and cycles.
  • Regression contract: Added source assertions for the empty-array terminator and deterministic reused-head wording.
  • External evidence: Replaced prose-formatted output with the exact JSON response, complete selected types, empty-array terminator, and observed state-set evidence in the PR body.
  • Calibration: Refreshed the progress skill digest while retaining medium effort.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using GPT Sol | 𝕏

Comment thread .claude/skills/progress/SKILL.md
`gh pr list --head` filters on the branch NAME only, and its own help states
that the `<owner>:<branch>` form is not supported. This repository is public,
so a fork's pull request carrying the same branch name comes back in that
listing. It could then be the sole OPEN row, and the walk would follow a
stranger's `baseRefName`.

The listing now requests `isCrossRepository` and drops every row where it is
true before any decision runs. An array left empty by that filter terminates
the walk exactly as a genuinely empty one does.

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

@pullfrog pullfrog 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.

Important

The new fork filter leaves one anchor path exposed, so integration discovery can still begin from unrelated fork metadata.

Reviewed changes Reviewed the fork-collision fix added since the prior Pullfrog review at 98c61a64.

  • Recursive head filtering: Added isCrossRepository to the all-state lookup, dropped fork rows before selection, and treated a filtered-empty list as the integration terminator.
  • Regression contract: Added source assertions for the recursive filter wording and refreshed the progress calibration digest.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using GPT Sol | 𝕏

Comment thread .claude/skills/progress/SKILL.md

@pullfrog pullfrog 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.

✅ No new issues found.

Reviewed changes Reviewed the repository-bound anchor fix and executable resolver added since the prior Pullfrog review at b56d1b5e.

  • Repository-bound anchor: Added isCrossRepository to the initial open-pull-request listing and prevented a same-named fork row from supplying the checkout's starting base.
  • Executable branch walk: Added resolveIntegrationBranch as a testable implementation of the documented all-state traversal, including fork filtering, reused-head selection, ambiguity, and cycle handling.
  • Regression coverage: Registered eight resolver cases, strengthened the progress hook assertion for the initial anchor, and refreshed the progress calibration metadata.

Pullfrog  | View workflow run | Using GPT Sol | 𝕏

@sonarqubecloud

Copy link
Copy Markdown

@thomasluizon
thomasluizon merged commit f8f0521 into redesign/main Sep 17, 2026
32 checks passed
@thomasluizon
thomasluizon deleted the chore/progress-generic-full branch September 17, 2026 20:25
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.

1 participant