chore(canary-phase5): pdf generator complete

Phase 5 ships the fourth generator in the canary registry. PDF canary
tokens are produced by in-place byte-replacing a 76-char fixed-width
placeholder in a committed template.pdf — the placeholder occupies a
direct dictionary value in the page's /AA /O /URI action (uncompressed,
not inside any Flate stream), so substitution preserves byte length and
keeps the cross-reference table valid.

Implementation commits in this phase:
  - c9c93332 feat(canary): pdf template (binary blob + build helper)
  - 13cd6cd1 feat(canary): pdf generator (byte-replace fixed-width URI action)
  - 73dd8373 feat(canary): register pdf generator in registry

Deliverables:
  - 477-byte committed template.pdf (sha256 70c63590…7e103), reproducible
    via go run ./cmd/buildpdftemplate -out ... (rebuild produces byte-
    identical output)
  - cmd/buildpdftemplate hand-builds the PDF-1.4 with computed xref byte
    offsets, validates via pdfcpu.api.Validate before writing
  - pdf.Generator: ErrTriggerURLTooLong sentinel, length-preservation
    guard, post-substitution bytes.Contains check, _ underscore padding
    (handoff overrides spec §9.4 line 1150 space padding — see anti-
    relitigation set), defaultFilename fallback for nil/empty/whitespace
    *string Filename
  - 22 top-level tests + 11 IP-precedence subtests + 4 filename subtests
    + 3 trigger-URL subtests = ~40 effective cases under -race. All 5
    spec-required tests present (LengthUnchanged, ContainsTriggerURL,
    PlaceholderRemoved, TooLongURL_ReturnsError, PdfcpuValidates) plus
    template-invariant + boundary + same-position regression guards
  - Registry expanded 3 → 4; pending list shrinks to {kubeconfig,
    envfile, mysql}

Audit outcome (2 agents in parallel per standing pattern):
  - superpowers:code-reviewer: PASS (0 BLOCKER, 0 SHOULD-FIX, 5 NITs all
    flagged by the agent as "not actionable" / "leave as-is")
  - general-purpose spec-adherence vs §9.4 + §8.5: PASS
    (0 BLOCKER, 0 SHOULD-FIX, 2 NITs: N1 was a miscount — pdf has 11 IP
    precedence subcases identical to docx, verified via grep; N2 was the
    Chromium/PDF.js /AA-stripping caveat, sanctioned-deferred to
    learn/04-CHALLENGES.md per the post-Phase-17 learn/ allowance)

No audit-fix commit needed this phase — zero actionable findings. Unlike
Phase 3 (a46fa446) and Phase 4 (10ae118d) which cleared real findings
before rollup, Phase 5 lands clean.

Pre-rollup acceptance gates clean at HEAD 73dd8373:
  - go build ./... + go vet ./... clean
  - go test -race -timeout=60s ./... — all packages pass
  - go test -tags=integration -race -timeout=300s ./internal/token/...
    ./internal/event/... — all pass
  - golangci-lint run ./... → 0 issues.
  - grep -rn "//nolint" --include="*.go" returns nothing (lint discipline
    inherited from Phase 0 supplement preserved)
  - BACKLOG.md open section still _(none)_

Forward state for Phase 6 (kubeconfig generator): different shape from
file-artifact generators — kubeconfig is YAML text + a path-based
/k/{id}/* handler that records kubectl method/path/UA and responds with
a Kubernetes-shaped 403. Trigger pattern departs from the /c/{id}+GIF
convention webbug/slowredirect/docx/pdf share.
This commit is contained in:
CarterPerez-dev 2026-05-13 13:58:55 -04:00
parent 73dd837362
commit 2856e2444f

Diff Content Not Available