Display public DEV conversations on blog posts
Purpose and success
Show the current public DEV conversation for each eligible syndicated post before the existing Utterances discussion. A successful implementation discovers the DEV article by exact canonical URL, renders every valid nested comment safely in API order, reloads fresh data without a deployment, and leaves the post and Utterances usable through every DEV failure mode.
Context and limits
.agent/specifications/display-dev-comments-on-blog-posts.mdis the approved behavioral contract._config.ymlmust define the single productiondev_to_username;dev_to: falseremains the per-post opt out.- The browser may issue unauthenticated DEV API v1
GETrequests only. It must never receiveDEV_TO_API_KEY. - Canonical URLs remain the identity contract. No article IDs, comments, snapshots, allowlists, browser storage, or scheduled synchronization may be introduced.
- The new page contract passes only the canonical URL and configured username from Jekyll to a browser module through
data-*attributes. - The current baseline has 24 passing DEV tests and validates 29 enabled posts, one opt out, and 1,287 tags. The repository-wide Prettier check has unrelated pre-existing failures.
Decisions
- Use a package-lock-pinned DOMPurify build served from
assets/js/vendorrather than a CDN or a custom sanitizer. This limits third-party requests and reduces XSS risk; recovery is reverting the integration and vendored artifact together. - Use JSDOM plus dependency injection for deterministic browser-module tests. Runtime initialization stays thin while fetch, DOM, sanitizer, and logger behavior can be simulated without the live DEV service.
- Render sanitized comment bodies as DOM fragments and validate every link and image URL after sanitization. API text and attributes are assigned only through DOM APIs.
- Add component styles to
css/override.css, which post pages already load, instead of changing Tailwind sources or generated CSS.
Milestones
- Centralize and validate DEV account configuration.
- Edit
_config.yml,scripts/dev-to/posts.mjs,scripts/dev-to/cli.mjs, and the focused post tests. - Require a non-empty string
dev_to_username, remove the synchronization hardcode, and use the validated value for account verification and public article lookup. - Run
npm run test:dev-toandnpm run dev-to:validate; both must exit 0 and validation must retain the current post and tag counts.
- Edit
- Implement tested browser discovery, sanitization, and rendering.
- Add DOMPurify, JSDOM, a reproducible
npm run build:dev-commentscommand, a locally served vendor module,assets/js/dev-to-comments.mjs, andscripts/dev-to/test/comments.test.mjs. - Cover eligibility, exact and duplicate canonical matches, pagination, zero comments, recursive order, safe links, malformed nodes, network failures, profile images, dates, and executable content with simulated responses.
- Run
npm run build:dev-commentsandnpm run test:dev-to; the vendor copy must be current and all tests must pass without live calls.
- Add DOMPurify, JSDOM, a reproducible
- Integrate presentation, privacy, and final validation.
- Add
_includes/dev-to-comments.html, update_layouts/post.htmland_includes/comments.html, style the component and print behavior incss/override.css, and updateprivacy-policy.html. - Emit no integration for
dev_to: false; otherwise reveal DEV content only after valid comments or the specified post-match retrieval fallback. - Build Jekyll, inspect enabled and opted-out output, exercise the real nested conversation on desktop and mobile in both themes and print preview, and run all repository validation commands.
- Add
Progress
- Approve the specification and map the current repository behavior.
- Confirm the test, metadata, formatting, API, and nested-comment baselines.
- Select locally served DOMPurify.
- Complete milestone 1: configuration is centralized and validated; 25 DEV tests and metadata validation pass.
- Complete milestone 2: local DOMPurify, deterministic browser behavior, and adversarial rendering tests pass in a 35-test suite.
- Complete milestone 3: Jekyll integration, presentation, privacy disclosure, real DEV rendering, responsive themes, fallback, and print behavior are validated.
- Perform the final specification audit and handoff: every approved behavior and prohibition is accounted for, with only the unrelated global Prettier baseline remaining red.
Risks
- Remote HTML can attempt XSS or DOM clobbering. DOMPurify, an explicit passive allowlist, URL validation, safe DOM assignment, and adversarial tests must all remain in place.
- DEV API shape changes and outages must not degrade the post. Discovery errors stay hidden; failures after a matched article show only the safe article fallback.
- Deep nesting and wide formatted content can overflow narrow screens. Semantic nesting remains complete while additional visual indentation stops after two levels.
- The vendored sanitizer can drift from its package. The test suite must compare the committed artifact with the installed distribution.
- The global Prettier baseline is red. Changed files must pass a focused check without broad unrelated formatting edits.
Documentation
privacy-policy.html is the public canonical disclosure. It must name DEV requests and DEV-hosted profile images, the possible exposure of IP address and user agent, DEV’s privacy page, and the new last-updated date. No new authoring metadata or publishing guide is needed.
Rollout and recovery
- Deploy as a normal static-site change with no migration, secret, workflow activation, or data recovery step.
- Verify
when-skill-evolution-means-removing-instructions.html, which currently has 17 DEV comments and nested replies, after deployment. - DEV outages are expected to fail in isolation. For a visual or security regression, revert the integration commit and republish; no local comment data exists to recover.
Validation
- Milestone 1:
npm run test:dev-topassed 25 tests,npm run dev-to:validatereported 29 enabled posts, one opt out, and 1,287 tags, and the four changed configuration files passed a focused Prettier check. - Milestone 2:
npm run build:dev-commentsrefreshed the pinned DOMPurify module,npm run test:dev-topassed 35 tests, metadata validation remained green, and the new package, copier, browser module, and tests passed focused formatting. - Milestone 3: the suite passed 39 tests after security and failed-image hardening; Jekyll built successfully with the image’s required
bigdecimalpreload; an eligible page rendered 18 live comments in seven reply groups while the opt-out and zero-comment pages emitted no DEV conversation. npm run build:dev-comments: exited 0 and the test suite confirmed the local DOMPurify module exactly matches the pinned package.npm run test:dev-to: passed 39 deterministic tests without the live DEV service.npm run dev-to:validate: exited 0 with 29 enabled posts, one opt out, and 1,287 tags.- Focused
npx prettier --checkfor every changed source and content file: exited 0; the vendor artifact and lockfile are intentionally ignored. npm run prettier:check: exited 2 with the same unrelated repository baseline, including the five known Liquid/HTML parser errors; no changed file appears in its warnings or errors.docker compose run --rm jekyll ruby -rbigdecimal -S bundle exec jekyll build: exited 0. The platform line Bundler adds locally was removed fromGemfile.lockafterward.- Generated HTML contains the loader only for eligible posts and orders the rendered DEV section before
Comments on this blogand the Utterances iframe. The opted-out post contains no DEV loader. - Headless Chrome rendered 18 current comments in seven nested lists on the real post. Desktop and 390 px mobile checks showed no horizontal overflow; light and dark screenshots were readable; print hid both discussions; reload issued fresh article and comment requests; a real zero-comment post stayed hidden; and a blocked comment request showed the safe DEV fallback while preserving the page.