Skip to content

Commit b1bb7dd

Browse files
committed
docs(12): record the Info-tier fixes in the code review fix report
1 parent e9f22f3 commit b1bb7dd

1 file changed

Lines changed: 81 additions & 12 deletions

File tree

.planning/phases/12-cloudflare-login-restoration/12-REVIEW-FIX.md

Lines changed: 81 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
phase: 12-cloudflare-login-restoration
33
fixed_at: 2026-07-23T00:00:00Z
44
review_path: .planning/phases/12-cloudflare-login-restoration/12-REVIEW.md
5-
iteration: 1
6-
findings_in_scope: 3
7-
fixed: 3
5+
iteration: 2
6+
findings_in_scope: 7
7+
fixed: 7
88
skipped: 0
99
status: all_fixed
1010
---
@@ -78,8 +78,62 @@ Added a regression test, `loginRequestPercentEncodesStructuralCharactersInCreden
7878
decoder splits a pair at its first `=` and leaves `+` untouched, so it reads a corrupted body as
7979
intact — which is why the existing baselines never caught this.
8080

81+
## Fixed Issues — iteration 2 (Info tier, at the user's direction)
82+
83+
Iteration 1 covered the `critical_warning` scope. The user then selected IN-01, IN-03, IN-04 and
84+
IN-05 for fixing and left IN-02 and IN-06 alone. Each landed as its own commit.
85+
86+
### IN-01: Comment overstates what the rejection throw prevents
87+
88+
`httpShouldHandleCookies = false` was set only on the clearance-carrying retry, so on the bare path
89+
URLSession filed the response's `Set-Cookie` automatically, before and regardless of the throw. A
90+
user with a live session who mistyped a re-login could have it clobbered by the failure page's
91+
tombstones. Rather than correct the comment, the stronger of the review's two options was taken: the
92+
jar is suppressed on both paths, which makes them symmetric and makes the comment true.
93+
94+
**Behavior change:** the bare login POST no longer sends jar cookies. Nothing is lost — credentials
95+
arrive on the *response* and `setCredentials` applies them on success — and the clearance path had
96+
already shipped on the same reasoning. `CloudflareChallengeDetectionTests` pinned the old value and
97+
was updated with the reason.
98+
99+
### IN-03: The four-attempt transport retry applies to the credential POST
100+
101+
`fetch` now takes an `attempts` count defaulting to the existing four, and `LoginRequest` asks for
102+
one. A POST the forum received but whose response was lost is otherwise replayed, and each replay
103+
spends another of the account's login attempts against the forum's own lockout — the lockout this
104+
phase taught the parser to surface.
105+
106+
The account-layer baseline pinning four attempts read them through `LoginRequest`, so it moved to
107+
`VoteGalleryTagRequest` and still guards the default; the login exception has a case of its own.
108+
109+
### IN-04: `redactedCredentialHeader` splits on `,` only
110+
111+
Hand-splitting the coalesced header was the root cause, so it is gone: `HTTPCookie` parses the
112+
header and the names come from the cookies it recognises. This closes both the reported noise
113+
(`expires` fragments printed as names) and the residual risk the review flagged — a non-compliant
114+
comma inside a value can no longer surface a fragment of that value, because a parser either
115+
recognises a cookie or yields nothing. The function now takes the response URL; without one it names
116+
nothing rather than gambling with a value.
117+
118+
### IN-05: `parseLoginErrorMessage` scans the entire page
119+
120+
The markers now count only as the text of the forum's own error-box label (`pformstrip` /
121+
`formsubtitle`), which is where both real shapes put them.
122+
123+
**Deviation from the review's suggested fix:** it proposed anchoring "before falling back to the
124+
page-wide scan". The fallback was deliberately not implemented — keeping it would preserve the exact
125+
false positive being removed, since a page with no anchor still reaches the page-wide scan.
126+
Unrecognised markup now degrades to the unlabelled generic failure the caller already handles, which
127+
leaves a live session intact; that is the safer of the two failure modes, because the misfire being
128+
removed drops session cookies on a login that actually succeeded.
129+
130+
One existing fixture (`markupAndEntitiesBetweenTheMarkerAndTheMessageAreIgnored`) used a class-less
131+
`<div>` and was updated to the real markup; three cases were added for the new contract.
132+
81133
## Verification performed
82134

135+
Iteration 1 (as recorded when written, before the iOS scheme was available to the fixer):
136+
83137
- Every modified file re-read after editing; `xcrun swiftc -parse` clean on all four.
84138
- No line exceeds the 120-character `line_length` limit; no `swiftlint:disable` was added and no rule
85139
was suppressed. SwiftLint itself is not installed on this machine, so rule conformance was checked
@@ -88,19 +142,34 @@ intact — which is why the existing baselines never caught this.
88142
- The new `dictString()` was type-checked and executed standalone: it emits
89143
`PassWord=p%26w%3Dd%2Bq%20x%25y&UserName=baseline-user` and `commenttext_new=first%0Asecond`,
90144
matching the updated test expectations exactly.
91-
- Full compilation was **not** run: `AppPackage` is iOS-only and `swift build` on macOS fails at
92-
manifest resolution (platform floors), before reaching any source. The test suite must be run
93-
through the iOS scheme during verification.
145+
- Full compilation was **not** run at the time: `AppPackage` is iOS-only and `swift build` on macOS
146+
fails at manifest resolution (platform floors), before reaching any source.
147+
148+
Iteration 2 closed that gap for both iterations, using the same scheme and flags as CI
149+
(`.github/workflows/test.yml`):
150+
151+
- `xcodebuild build -scheme EhPanda` against an iOS Simulator destination: **BUILD SUCCEEDED**, no
152+
errors and no compiler warnings. SwiftLint runs as a build-tool plugin in this project, so a clean
153+
build is also a clean lint — no rule was suppressed and no `swiftlint:disable` was added.
154+
- `xcodebuild test -scheme EhPanda`: **TEST SUCCEEDED**, all suites green. The pre-existing
155+
`withKnownIssue` blocks remain the only reported known issues.
156+
- Every test added or changed across both iterations was confirmed by name in the run output.
157+
- `scripts/check-cookie-logging.sh` passes.
158+
- `-skipMacroValidation` was required: three macro packages need re-approval after version bumps.
159+
This is a local Xcode trust prompt, not a code issue, and CI passes the same flag.
94160

95161
## Out of scope
96162

97-
IN-01 through IN-06 are Info-tier and were not addressed under `fix_scope: critical_warning`.
98-
IN-01 (the comment overstating what the rejection throw prevents) and IN-04 (the DEBUG redactor's
99-
`,`-only split) sit in `Request+Account.swift`, which this run touched for WR-03; they were left
100-
untouched so each commit stays scoped to its finding.
163+
IN-02 and IN-06 were reviewed and deliberately left alone.
164+
165+
- **IN-02** (stale dismissal echo misread as a swipe) — the review concludes the window is
166+
unreachable in production and records it as a known limitation rather than a defect. Closing it
167+
would mean threading a challenge ID through the presentation for no observable benefit.
168+
- **IN-06** (locale-dependent assertions in `AppErrorStructuredTests`) — environment-sensitive
169+
rather than wrong, pre-existing test style, and CI runners are English so it never fires there.
101170

102171
---
103172

104173
_Fixed: 2026-07-23_
105-
_Fixer: Claude (gsd-code-fixer)_
106-
_Iteration: 1_
174+
_Fixer: Claude (gsd-code-fixer, iteration 1) / Claude Opus 4.8 (iteration 2)_
175+
_Iteration: 2_

0 commit comments

Comments
 (0)