Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 13 additions & 14 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,23 @@
Loose, personal roadmap for the CodesWhat homepage. Not a ticket tracker (that's
work). Just the ideas worth not forgetting.

## Where things stand (2026-10-25)
## Where things stand (2026-10-25, evening)

The Catalogue redesign, the `/support` page and the analytics events shipped on
2026-10-22 (#69, #70, #71). Production is `main@68520ee`. Two things are in
flight:

- [ ] Dependency security patch (Next 16.4.3, dompurify, sharp): PR #72 is green
on CI and preview and waiting on a delivered review before it lands in `dev`,
then a promotion to `main`.
- [ ] CI hardening (support tests in `verify`, no duplicate promotion runs,
fork-safe skip, no persisted checkout credential, actions pinned to
commits): finished and tested locally, queued behind #72.
- [ ] Remove the stray `visual-schedule-manager-portfolio/` app and root
`start.sh`; nothing references either.
2026-10-22 (#69, #70, #71). On 2026-10-25 the dependency security patch (#72:
Next 16.4.3, dompurify 3.4.17, sharp 0.35.5, which cleared every open Dependabot
alert) and the CI hardening batch (#73: support tests in `verify`, no duplicate
promotion runs, fork-safe skip, no persisted checkout credential, actions pinned
to commits, stray `visual-schedule-manager-portfolio/` and `start.sh` removed)
were promoted together in #74. Production is `main@722af11`, checked in a
browser after the deploy.

Still open:

- [ ] Clean up `test/posthog.test.ts`, which imports a posthog-js internal path,
then bump `posthog-js` and add `test:posthog:browser` to CI.
- [ ] Close #58 (`.claude/` is already ignored on `main`). #57 is partly done;
the remaining items are tracked privately.
- [ ] #57 is partly done (pins, credentials, CSP and input tests); the remaining
items are tracked privately.

## Progressive-enhancement fallback for the 3D layer

Expand Down
6 changes: 6 additions & 0 deletions frontend/README-VERCEL.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ and persists after reload, the specimen plates render, and `/support` shows its
buy links. Build success and analytics events alone do not establish that client
initialization worked.

Then run the PostHog response contract against the canonical domain:

```sh
POSTHOG_BROWSER_BASE_URL=https://codeswhat.com npm run test:posthog:browser
```

`npm run og` regenerates the share cards in `public/og*` with playwright-core
and the locally installed Google Chrome; it isn't part of the build.

Expand Down
22 changes: 21 additions & 1 deletion frontend/test/posthog-browser.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,26 @@ test("production browser response keeps PostHog on the exact proxy", {
assert.match(csp, /script-src[^;]*https:\/\/e\.codeswhat\.com/);
assert.match(csp, /connect-src[^;]*https:\/\/e\.codeswhat\.com/);
assert.doesNotMatch(csp, /\*\.posthog\.com|https:\/\/(app|us)\.posthog\.com/);
assert.doesNotMatch(body, /query-secret=should-not-leave/);
assert.match(
response.headers.get("cache-control") ?? "",
/\b(private|no-store)\b/,
"HTML reflects request data (Next.js flight payload), which is only safe if the page isn't shared-cached",
);
// Next.js inlines router state, including the request's own query, in
// self.__next_f.push(...) scripts. Only scripts whose body starts with that
// call are excluded; everything else must not reflect the query.
const flightCall =
/^\(self\.__next_f\s*=\s*self\.__next_f\s*\|\|\s*\[\]\)\.push\(|^self\.__next_f\.push\(/;
let flightScripts = 0;
const withoutFlightData = body.replace(/<script\b[^>]*>([\s\S]*?)<\/script>/g, (block, inner) => {
if (!flightCall.test(inner.trim())) return block;
flightScripts += 1;
return "";
});
assert.ok(
flightScripts > 0,
"expected Next.js Flight scripts in the HTML; the reflection exclusion is only valid when they exist",
);
assert.doesNotMatch(withoutFlightData, /query-secret/);
assert.match(body, /<script\b[^>]*\bid="theme-init"/);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
});
10 changes: 9 additions & 1 deletion frontend/test/posthog.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,20 @@ import {
} from "../lib/posthog-privacy.ts";

const require = createRequire(import.meta.url);
const { PostHog } = require("../node_modules/posthog-js/lib/src/posthog-core.js") as {
// Deep import on purpose: the published main bundle minifies `_runBeforeSend`
// away, while lib/* is a published file in posthog-js's package.json `files`
// with no exports map.
const { PostHog } = require("posthog-js/lib/src/posthog-core.js") as {
PostHog: new () => {
config: { before_send?: (input: unknown) => unknown };
_runBeforeSend: (input: unknown) => unknown;
};
};
assert.equal(
typeof new PostHog()._runBeforeSend,
"function",
"posthog-js/lib/src/posthog-core.js no longer exposes PostHog#_runBeforeSend; the before_send pipeline contracts below need updating for this posthog-js version",
);

// posthog-js attaches these to every envelope by default (PostHog/posthog-js
// packages/browser-common/src/utils/event-utils.ts, getEventProperties()).
Expand Down
Loading