A1 - the four-gate line said `internal/api ~11.0s 含真库集成`. It does not
include the real-database integration tests: without TEST_DATABASE_URL they
skip silently, and the 11s is the mock path. The wording mattered because it
hid the fact that every run in this batch - implementer, task reviewer,
fix-forward and controller alike - skipped them, including the two
concurrency/TOCTOU guards that §1.6 names as the behavioural evidence for
phase six. Replaced with the measured picture: 4 skips in internal/api, 26
`--- SKIP` lines repo-wide, and the full list of the 11 Test functions gated
on that variable (cmd 2, api 4, repository 2, service 3). Also recorded the
branch review's DB-parity run - base fe810dd 194 PASS/0 FAIL versus HEAD
b15c2a8 199 PASS/0 FAIL, +5 being exactly this batch's new guards, state
multisets identical - which is the first runtime proof of zero behaviour
change on the postgres path; everything before it was static. The
pre-merge checklist now has to include a DB-enabled comparison or the next
batch skips them again.
A3 - fix-evidence/rv-t3-v2-filtered.py prints 6 ORDER VIOLATIONS while the
fix report's prose says both trees have 0. The prose is right and the script
is the artefact: it assigns repeated text lines to destinations with a greedy
pop(0), so identically-named lines land in the wrong bucket. The branch review
redid the check with an unambiguous-unique-text method and got pre=0, post=0.
Annotated the archived script in place (it lives in the gitignored evidence
area, kept for auditability) so nobody cites its violation count as evidence
again, and pointed at the review's §12 reproduction method.
Post-write checks: 20/20 assertions, 12 table blocks with 0 column anomalies,
8 code fences paired, 28 headings with no duplicates, and the annotated script
still passes py_compile.
The full-branch review of chore/p15a-approve-phases returned APPROVE with
notes: the merged branch code itself has zero defects, but four places in this
spec disagreed with the code it describes. All four came from the controller
writing executable details from memory during the first re-pin.
N1 - applyApprovalInTx parameter order, three places (D-C row, the §3.1
skeleton call site, the §3.2 phase-six signature). The spec said
`plan approvePlan, submissionID`; the code has always been
`submissionID string, plan approvePlan`. The implementer's order is legal and
better: a scalar identifier before an aggregate matches Go convention.
N2 - §3.2 migration ranges the first re-pin claimed to have fixed but had not:
48-85 -> 48-86 (86 is the closing brace of the coverUpload block) and
87-100 -> 88-101 (87 is a blank line, 88 is `switch sub.Kind`).
N3 - the §1.2 difference-table header: phase four 87-100 -> 88-101, phase six
136-186 -> 136-199 (186 is a mid-branch line inside MetadataChange; 199 is the
closing brace of the switch).
N4 - §T1.4 said the CG2 needle's only hit is `:196`, which is the pre-F9 line
number from dda3fe8; HEAD measures `:201`. The (b) blind-spot note said
"126-200 = 77 lines"; 126-200 is 75 lines - 77 belongs to the §1.1 range
125-201 and was carried over by mistake.
Every corrected value was re-derived from the base tree fe810dd and HEAD
b15c2a8 line by line rather than copied from the review, and the two
derivations agree. A RE-PIN note at §3.2 phase six records the true values and
the lesson, which §8 discipline 11 now states as a rule: executable details in
a spec (regexes, signatures, literals, line ranges) must be grepped out of the
code entity and pasted, never hand-written.
Post-write checks: 20/20 assertions, table column counts unchanged (12 blocks,
0 anomalies), 8 code fences paired, 28 headings with no duplicates.
Closes N1-N4 of crearte-server/.superpowers/sdd-p15a/final-review-report.md.
N5 (the verification artifact's fake rigor) is closed separately in the
gitignored evidence area: verify-fix-v8.py now runs 106 real checks with zero
bare prints, and the v2 output it cites is archived on disk.
Twenty-four pinned corrections across the two specs and their plans, every one
traced to a measured finding. Both task-level reviewers overturned claims I had
written into the specs, and I reproduced each against the base tree before
accepting it (git show, never the working tree, which already carries the fix).
P15-A spec gains two sections. T1.4 records the reviewer's candidate guard for
phase-independence: the spec called "do not merge phases four and six" the most
important constraint in the batch, yet its only stated mitigation was the
byte-level comparison, which is one-shot evidence — after the report is filed,
nothing reddens when someone merges them. The reviewer built CG1/CG2/CG3 and
verified them both ways: green on the legal tree, red on four distinct merge and
degeneration forms, all assertion-red rather than compile-red, while the three
shipped guards stayed green throughout. T4 records the adjudication of nine
findings, including two that undercut the guard the batch itself added: a
half-finished dir parameter that redirects which files are counted but not which
are read, so a scan pointed elsewhere can satisfy its own precondition while the
169-line function it exists to catch stays invisible; and a span scanner keyed to
line-initial func, which means a 125-line package-level closure passes all four
gates and all three guards. Also pinned: the gofmt gate in the four-gate recipe.
gofmt -l lists unformatted files and still exits zero, so the recipe everyone ran
reported gofmt_exit=0 as evidence of formatting cleanliness. Implementer and I
shared that recipe, which is why the same blind spot was computed twice and
caught by neither.
Phase intervals are corrected to the real base-tree line numbers. The prior text
told the implementer to move lines 212-213 into a helper; 213 is the slog.Info
the same spec requires to stay in the caller, so following it would have swapped
what moves with what stays. Phases six and seven also overlapped, each claiming
the transaction error mapping. The skeleton now passes a pointer where its own
note eighteen lines later demanded a pointer receiver, and the transaction
helper's signature carries submissionID, which removes the batch's dependence on
a cross-repository equality argument for the only acceptance criterion it has.
P15-B spec corrects the artifact criterion I had backwards. I wrote that deleting
a token should change the var(--color-*) count; that count measures usage, the
token had zero usage across seven utility suffixes, and it stayed at 75. Had it
moved, that would have meant something referenced the token, contradicting the
dead-token premise. Definition-side and usage-side are separate measures and the
spec now says so.
Leg five is re-pinned as critical. It was the sole enforcement point for the
non-reactive injection decision, and it only pinned literal form: two
type-legal variants that establish the dependency elsewhere in the computed body
pass all ten legs with vue-tsc clean, and the reviewer proved by effectScope
evaluation counting that both really do make the iframe src flip on a theme
change, reloading a running game. The narrowing was introduced by the
implementer's own fix, to avoid a trap comment that spells out the forbidden
literal; but comment masking already neutralizes that comment, so the narrowing
was unnecessary and the second line of defense created the gap. Same shape as
the P14 final review finding a hole in the first round's fix.
Also pinned: the rejected deviation five, with the corrected root cause (all four
freeze attempts used page.route, which does not intercept service worker
registration, worker-issued requests, or navigations synthesized by respondWith;
context.route holds the loading screen past 25 seconds); the missing
dark-system-times-light-hash grid that one mutation slipped past all ten source
legs and all five e2e legs simultaneously; the fourth tautology class the spec's
three warnings omitted, where emptying a loop's driving collection makes it
vacuously true; legs seven and eight, which respectively substring-searched a
hex that occurs three times in the file and matched only double-quoted style
attributes; the copyable code block's comment, which now avoids the three
literals that would false-redden a correct implementation, with the annotation
moved outside the block; and three new discipline entries covering untested
method scope, measurement units, and retaining one-off verification scripts.
Every correction was checked by a script asserting both directions — the pinned
text present and the superseded text gone — plus table column integrity, fence
pairing, and code-block cleanliness. Three earlier attempts at this re-pin failed
on my own errors and were fixed before commit; the working tree was never left in
a half-edited state.
Two batches, one per repo, so they can run in parallel under the one-writer-per-repo
rule: P15-A refactors crearte-server's Approve function, P15-B fixes a dark-mode gap
in crearte's game loading screen plus two guard items.
P15-A splits a 169-line function whose seven phases are mapped here with real line
numbers — the base-tree mapping logged during P14 is void, since that batch moved the
function into moderation.go. The spec's central constraint is that phases four and six
look alike but must not be merged: four is an optimistic pre-check that runs unlocked
before any object copy, six is a pessimistic re-check under a row lock after the copies
have happened. They differ in four concrete ways (kind coverage, the NewVersion runtime
and bundle checks, whether entities are created, and the error wrapping), so the
apparent duplication is deliberate TOCTOU defence rather than removable redundancy.
A measurement also overturned the survey's claim about helper shape: both an unexported
method and a package-level function leave all seven P14 assertions green, because
IsExported filtering and NumMethod's exported-only view put neither in scope. The
choice is therefore about needing receiver fields, not about the guard.
P15-B fixes a gap P13 left: its token work covered only the src/index.html entry and
missed the second Vite entry, bootstrap, so the game loading screen hardcodes light
values and dark-theme users see a white flash on every virtual game launch. The fix
rides the hash channel bootstrap already parses rather than inventing a postMessage
protocol, which would be async and so repaint light first — the very flash being
removed. The spec records the trap that makes this easy to get wrong: GameHost builds
targets in a computed, so injecting the reactive theme ref would make a theme switch
recompute the iframe src and reload a game in progress; the non-reactive snapshot is
required, and a guard leg plus a mutation exist solely to hold that line. It also
records two pre-existing contrast violations found on the way, where inline style
attributes override conforming stylesheet values with lower-contrast ones, and why
noHardcodedColor cannot be extended to cover this file: it only collects .vue and
blanks out style blocks, so widening its roots would scan nothing while looking
like coverage.
Every line number and quoted signature in both specs was checked against source
before commit; two claims the survey had asserted from reasoning were measured
instead, and one of them was wrong.
The branch review approved with notes but overturned two universal claims I had
already committed to the spec. Both overturns were independently reproduced
before acting on them, and both were correct.
FR-1 (important): assertion 5's source scan required a NAMED receiver, but Go
permits omitting an unused one, and a shadowing body is exactly the case that
often needs none. Under func (*ContentService) CoverURL(...) the old pattern
matched nothing, so build, vet and all seven assertions stayed green while the
shadow was effective. The regex literal in §5-T1.5 is corrected to the optional
group now in the code, and the claim that assertion 5 is the only mechanical way
to catch shadowing is scoped: it was false before the fix, and after it the
assertion's reach is wider than the original text said, because NumMethod()
exposes only exported names — so an unexported root method is also caught by the
source scan alone.
FR-5: my adjudication of F4 said the two layers 'cover completely'. That is a
universal claim and an orphan private helper on the wrong line is its
counterexample (assertion 2 filters on IsExported, and Go does not report unused
methods). The wording is narrowed to what the layers actually cover, with the
reason the verdict still holds: the third layer can only ever produce dead code.
Both are recorded as instances of the same defect I keep committing — stating a
conclusion before establishing its range. §8 rule 5 already required universal
claims to have universal-range evidence; these were two places I did not follow
my own rule.
The lesson is written into the spec rather than only the ledger: when claiming a
guard is the only thing that catches a failure mode, enumerate the forms first
(named/unnamed receiver, value/pointer, exported/unexported), or the claim
becomes the next reviewer's counterexample.
The task-level review rated the split PASS with notes and found that the
architecture guard did not cover one of the three purposes spec D-J states for
it. Six places are corrected here before the branch review reads the spec, so
the reviewer works against a frozen target rather than a moving one.
- D-J is amended from three assertions to seven. The original three each have
teeth (verified by mutation) but together they missed an entire dimension:
nothing enumerated the composition root's own method set, so adding a method
there left all three assertions PASS, go build/vet clean, and the full
11-package suite green. The god object could regrow silently.
- The mutation list gains d through h, and mutation (a) is annotated with the
bypass the review found in my own positive control: (a) turns red because it
REMOVES CoverURL from CatalogService, not because anything detects the extra
root method. Rewritten as a copy that keeps the original — the shadowing form
(e) — mutation (a)'s design would have passed green.
- Two fixes the review proposed are recorded as rejected, with the spike
measurements, so they are not retried: asserting NumMethod()==0 on the value
type is unsatisfiable because embedding *T puts its methods in both method
sets (the legal tree already reports 22 — vacuously false, the mirror image of
P13's vacuously true assertion), and Method.Func identity cannot detect
shadowing because promoted methods are forwarding wrappers that already differ
on the legal tree (5153408 vs 5148192 unshadowed, 5148288 vs 5148192
shadowed).
- Line counts are pinned to the wc -l convention, verified against the same
convention used for the 856 baseline and auth.go's 234. The metrics table gains
a measured column: content.go 82, largest of the six files 298 (moderation.go,
not submission.go as the spec predicted).
- D-D records that ErrSubmissionConflict is also used by AccountService at
account.go:136, outside the submission and moderation lines, which makes the
SHARED ruling necessary rather than merely correct.
- The risk table gains two Route C properties that were previously only in
controller notes: the root has no named fields so s.objects is an ambiguous
selector (a compile-time barrier earlier than the guard, and the reason the
guard is the ONLY barrier against adding a concrete field), and go vet stays
silent on a root method shadowing a promoted one (vet_exit=0 measured), which
is why assertion 5 cannot be replaced by a reflect-based check.
Registers the next batch before implementation starts, so the design is
versioned and reviewable rather than living only on disk.
Scope: crearte-server only. content.go is 856 lines / 30 functions, the sole
outlier in internal/service (next largest production file auth.go is 234 lines;
content.go alone is 22% of the directory) and carries five unrelated
responsibility lines in one struct whose five fields are all shared repository
dependencies with no mutable internal state.
Three survey findings make the split low-risk rather than aspirational:
- the seven cross-line calls all target package-level helper functions, with
zero method-to-method cross-line calls, so splitting creates no cross-service
callbacks and no import cycle;
- each of the five handlers uses exactly one line's methods with zero overlap,
so each dependency face narrows from 22 methods to its own 2-6 with no adapter;
- 11 of the 19 test construction sites only construct and never call methods,
and a Go embedding spike (H1-H4, run in golang:1.24-alpine, vet clean)
confirmed the promoted method set satisfies consumer-defined narrow
interfaces — so the composite root keeps every construction site and all five
serve.go wirings unchanged while handlers still narrow.
The survey also found ContentService.now is a dead field: zero s.now references
in the file, no test seam injecting it, while the sibling services that share
the pattern do use theirs (auth.go reads s.now(), cleanup.go has SetNow). It is
removed as part of the split rather than being assigned a line.
Two risks were falsified by measurement before designing: no code inside the
package reads ContentService's private fields (AccountService holds
repository.ContentStore, not *ContentService, so it is untouched), and the type
is never interface-ised, type-asserted, or used as a method value.
Deliberately out of scope: the 170-line Approve function (its seven phases are
mapped and logged for a later batch — one concern per batch), memory_content.go
(814 lines but a test double with zero non-test references), and handler
error-mapping dedup (92 WriteError sites but only 2 errors.Is checks, so the
duplication does not justify itself).
Verification plan: four gates in the dockerized toolchain plus structural
metrics (content.go under 120 lines, largest of the six files under 300, zero
service.ContentService references left in handlers, zero existing test files
modified) and three mutations the new architecture guard must fail on.
Brings the P13 batch into this wrapper's version control: spec and plan enter
the repo, ROADMAP gains the P13 row and its document-index entry, and the four
stale 'C dark mode' backlog mentions carried since P9/P10/P9-B/P12 are marked
cleared in place — following the P12 precedent of annotating the historical row
rather than rewriting it, since those entries were true when written.
Wrapper CHANGELOG 0.3.6 records the crearte-only dark-mode delivery
(983e7c4, merge-base e59a171), the parallel server micro-batch delivered during
the survey phase while the implementer owned crearte exclusively (dbf7fe5,
0.17.2: a real uploads.go error-fallthrough defect with zero prior coverage,
plus a decision-record comment on the bundle-key route after grep overturned the
initial suspicion of a hole), and three lessons:
- max(a,b) >= k is a vacuous-assertion hot zone: when the two sides are
complementary the max has a non-trivial lower bound (here sqrt(16.50) =
4.0621), so any threshold below it can never fail. The controller's own first
correction to the scrim guard shipped exactly that, and the same
one-directional verification recurred in the alpha fallback. Fixing a guard
now requires proving both no false-red on legal values and no false-green
under mutation.
- Tailwind v4 scans every source file including test files, so a class-name
literal in a test comment burns a dead utility into the artifact.
- A universal claim needs a universal grep: 'accent is the only background use'
was false because both the spike-0 grep and the new guard covered app/ while
runtime/ sits beside it. That blind spot cost a false spec fact and hid a real
pre-existing WCAG violation (paper on accent = 3.2590 in light, since P9-B).
ROADMAP's P13 row cites merge commit 983e7c4 per the P12 convention, with the
bookkeeping commits named separately so the reference cannot be mistaken for
them.
Register the P12 spec and plan in the ROADMAP doc index, add the P12 batch row,
and update two backlog lines that this batch's measurements settled:
- The 'mobile header / hamburger menu' backlog entry carried since P9-B is
falsified and removed: nav content width is only 74-158px and measures zero
horizontal overflow at 320/360/375/412/768/1280px. Building it would have
shipped a hamburger menu nobody needs.
- Nav links wrapping per-character at phone widths stays parked pending a
design decision, not a CSS tweak: it is cosmetic only (no overflow, no
clipping, no lost function), whitespace-nowrap costs +11px at 320px, and all
seven gap-reduction variants measured fail at 320px + long name because
logo 77px + nowrap nav 138-158px + truncated name 96px do not fit. It
competes for the same pixels as the header fix.
- The P11 polish backlog is partially retired: three of P9-B's four items plus
P11-N5/N6 are closed by this batch; the remaining nine P11 minor notes are
documentation-only with no actionable change.
Merged and pushed: crearte e59a171 (0.25.0), crearte-server d56c593 (0.17.1),
crearte-deploy 529a988 (0.7.1). Three task-level reviews plus a whole-branch
final review verdict APPROVE with notes, zero critical and zero important; all
notes adjudicated before merge. The final reviewer independently reproduced
three mutations rather than accepting the controller's claims.
ROADMAP P11 row -> 完成 with the D-A->D-A' re-pin narrative; doc index updated;
CHANGELOG 0.3.4 (Done section, bilingual) recording the endpoint-verification
catch: the original subnet-trust design was a rate-limit bypass under compose's
docker SNAT, corrected to an empty TRUSTED_PROXIES default. Register the P11
spec + plan in the doc index.
- docs/specs/2026-09-29-author-page-design.md: /users/:user pure
aggregation page, minimal list, zero backend/migration changes
- docs/ROADMAP.md: P4 scope refined per design (frontend filtering
instead of API author filter), register spec in doc index