DataTalksClub / DataTalksClub/website

Restore whitespace-neutral Wiki and Person prose after link localization

Open
#271 13 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

accessibility bug content frontend P0 testing
Dominant language
Python
Stars
0
Forks
0
PR merge metrics
No merged PRs in 30d

Description

Product outcome

Restore whitespace-neutral public prose for both Wiki pages and Person profiles after 912f60fff9aed1dfd306fe9eba9d465674976dcc added safe localized Markdown-link rendering. A visitor sees exactly the source-authored text and line breaks—without template indentation/newlines before or after it—while links, escaping, copy, headings, routes, projection data, CSS, and DOM semantics remain unchanged.

PM disposition: proceed as one bounded P0 shared-template correction. The defect and accepted fix are fully derivable; no content or design decision is required.

Normative authority and evidence

  • URL, link, and SEO compatibility: exact Wiki/Person routes, fragments, internal/external links, rendered content, and crawlable parity.
  • GitHub content and people: Podwiki/Person source ownership, sanitized content, exact relationships, and projection stability.
  • Security, privacy, accessibility, and operations: escaped/sanitized source output and semantic/accessible public pages.
  • Verification strategy: Wiki/linked-Person navigation, URL/link parity, accessibility, desktop/mobile, and no-JS behavior.
  • Exact regression anchor: face8e4808d65afbf0374d1ced7a88079950d663.
  • Introducing change: 912f60fff9aed1dfd306fe9eba9d465674976dcc changed templates/public/_prose_body.html from inline paragraph/list values to multiline {% if block.markdown %} branches.
  • .prose-paragraph { white-space: pre-line; } intentionally preserves source line breaks, so template-authored indentation/newlines become visible. The focused Wiki module collected 46 tests and reported 28 failures/1 skip on the exact red anchor. This is diagnostic evidence, not reusable success.
Exact checked census
Consumer / branch Plain Markdown Total
Wiki paragraphs 10,665 19 10,684
Wiki list_item blocks 2,907 0 2,907
Person paragraphs 662 0 662
Person list_item blocks 5 0 5
Non-heading total 14,239 19 14,258

Additional fixed inventory:

  • Wiki: 282 records, 15,746 blocks, 2,155 headings, 13,591 non-heading blocks.
  • Person: 438 records, 667 blocks, no headings, all 667 non-heading.
  • All 19 current Markdown blocks are Wiki paragraphs with accepted localized internal links; current Markdown list and Person-Markdown cells are empty but remain supported template states and require synthetic regression coverage.

Scope

1. Make the four text branches whitespace-neutral

Change only templates/public/_prose_body.html so each of these values begins immediately after its opening <p> and ends immediately before </p>:

  1. plain paragraph: block.text with Django autoescaping;
  2. Markdown paragraph: block.markdown|public_text;
  3. plain list item: block.text with Django autoescaping;
  4. Markdown list item: block.markdown|public_text.

Use branch-local inline element serialization (duplicating the small <p> wrapper where necessary). Do not solve this with CSS, JavaScript, a global whitespace-stripper, {% spaceless %} around the whole partial, Python post-processing, strip, join, projection mutation, or a changed template filter. Only template-authored boundary whitespace is removed.

For every non-heading block:

  • the serialized element has no template-authored \n or indentation between the opening tag and first source/rendered node, or between the last node and closing tag;
  • a checked current block whose source begins/ends with a non-whitespace character has an innerHTML and textContent with no leading/trailing template whitespace;
  • source-authored internal spaces, leading/trailing whitespace in a synthetic direct-template fixture, repeated spaces, Unicode, tabs/newlines, and blank lines are not trimmed or normalized;
  • public_text retains its current source-line-to-<br> conversion and conservative link rendering.
2. Preserve links and escaping exactly

core.templatetags.public.public_text remains unchanged.

  • Safe root-relative Markdown links, including query and fragment, retain class="app-link", exact escaped href, visible label, and no external target/rel/screen-reader suffix.
  • Safe HTTP/HTTPS external Markdown links retain class="app-link", target="_blank", rel="noopener noreferrer", and exactly one (opens in a new tab) screen-reader suffix.
  • Text before, between, and after links remains in source order. Multiple links and source newlines remain exact.
  • Plain and Markdown source containing &, <, >, quotes, encoded entities, tag/script-looking text, or unsafe/non-matching Markdown syntax remains inert and escaped under the existing filter/autoescape contract. No safe is added to plain text, and no Markdown parser/grammar is broadened.
  • Across the 19 checked Markdown paragraphs, the unchanged conservative grammar renders exactly 18 accepted root-relative anchors. The remaining source token [governance](#governance-and-project-boundaries) intentionally remains escaped literal text because fragment-only relative destinations are outside that grammar. Preserve all 19 blocks, the exact 18 anchor labels/destinations/order, and that one inert source token; do not broaden or reinterpret the filter.
3. Preserve structure and presentation
  • One input block still creates exactly one direct child in source order.
  • Wiki heading level 2 remains <h2 id=...>; every other heading remains <h3 id=...>. All 2,155 checked IDs/text/order remain exact.
  • Paragraph/default/unknown non-heading kinds remain <p class="prose-paragraph">.
  • list_item remains the existing <p class="prose-item"> display-list-item contract; do not introduce <ul>, <ol>, or <li> in this issue.
  • Do not add a wrapper, ARIA role, focus stop, hidden node, <br>, empty paragraph, or whitespace-only visible line. Existing parent landmarks, .prose wiki-prose/Person prose wrappers, heading hierarchy, canonical/SEO metadata, relationships, and navigation remain unchanged.
  • templates/core/_design_system.html, including white-space: pre-line, remains unchanged. Source-authored line breaks must still render.

Allowed files

Product source:

  • templates/public/_prose_body.html

Focused Django/source tests:

  • content/tests/test_wiki_design.py
  • content/tests/test_person_profile.py
  • content/tests/test_public_template_source.py

If persistent browser assertions are not expressible in existing coverage, one focused playwright_tests/test_public_prose.py may be added; otherwise existing playwright_tests/test_foundation_smoke.py and playwright_tests/test_accessibility.py run unchanged. Do not edit those broad files merely to change unrelated snapshots.

No other product/template/helper/CSS/projection/test path is allowed without returning to PM. In particular, templates/public/_prose_blocks.html belongs to the separate article/long-form contract and #261; it is not part of #271.

Dependencies and ordering

No open issue supplies a required interface, model, or decision. #271 is independently engineer-ready.

  • #253 remains blocked source/projection recovery, but its clean two-regeneration evidence found wiki.json and wiki_search.json byte-identical. #271 changes neither and does not bless #253's other divergent artifacts.
  • #261 changes the distinct templates/public/_prose_blocks.html; no source path overlaps. Each lane still uses its own isolated worktree and frozen verification evidence.
  • #223/#270 podcast URL policies are unrelated.

Normal orchestrator rebase/order applies before final testing; it is not a product dependency.

Acceptance criteria

  • All four plain/Markdown × paragraph/list branches emit no template-authored leading/trailing whitespace and preserve every source-authored character/line break.
  • The exact 282/15,746/2,155/13,591 Wiki and 438/667 Person inventories render one block to one ordered element; the 14,258 non-heading elements have no boundary whitespace regression.
  • All 19 checked Markdown paragraphs preserve exact visible text: exactly 18 root-relative anchors retain label/destination/order and [governance](#governance-and-project-boundaries) remains one inert escaped literal; all 14,239 plain blocks preserve exact escaped text.
  • Synthetic Markdown list, multiple-link, internal query/fragment, external link, source newline/blank-line, source boundary-space, Unicode/entity, and tag/script-looking inputs preserve the accepted filter/escaping behavior without injection.
  • Heading tag/ID/text/order, paragraph/list classes and element types, direct-child count/order, wrappers/landmarks, canonical/routes/SEO, and accessibility tree semantics remain unchanged.
  • No CSS/JavaScript/filter/helper/projection/source/pin/manifest/hash/copy/route/model/migration/dependency change occurs.
  • Focused source/Django/browser/accessibility tests and fresh full format/lint/type, Django, Playwright, screenshots, content/link/SEO invariants, migration drift, container, and versioned verification gates terminate green on one frozen candidate. No assertion weakening, whitespace normalization of expected content, retry, skip, xfail, quarantine, timeout extension, partial run, or unchanged-red evidence is success.
  • Engineer and independent tester record exact base/candidate/diff/plan/run/report/screenshot digests and terminal counts; tester reads every required screenshot and PM evaluates visible copy/link/list/heading behavior before acceptance.

Required focused scenarios

  1. Render the partial directly with synthetic plain paragraph, Markdown paragraph, plain list item, and Markdown list item. Assert exact serialized opening/value/closing boundaries and exact source character preservation.
  2. Plain source with & < > " ', encoded entities, <script>/event-handler-looking text, Unicode, repeated spaces, tabs, one/multiple/blank lines, and deliberate source boundary spaces: inert escaped output, exact source whitespace, no template whitespace.
  3. Markdown with one/multiple internal links, query/fragment, external HTTP/HTTPS link, text around links, source newlines, encoded entities, unsafe/non-matching syntax, and script-looking labels: exact existing DOM/attributes/escaping and one element only.
  4. Heading level 2/other, paragraph, list item, and unknown-kind fallback in one ordered fixture: exact direct-child sequence, tags/classes/IDs/text, no wrapper or empty/whitespace-only rendered line.
  5. Current full census: 282 Wiki/438 Person pages or deterministic rendered fixtures cover all 15,746/667 blocks; assert exact kind counts, 19 Markdown blocks producing exactly 18 root-relative anchors plus one inert fragment-only source token, no unexpected leading/trailing boundary whitespace, and no changed heading/link/cardinality.
  6. HTTP route coverage for /wiki/ai-tools-for-personal-productivity (plain + Markdown paragraph), /wiki/apache-airflow (plain/Markdown paragraphs + list items), /people/aleksandrkim.html (plain paragraph), and /people/jacquespeeters.html (plain paragraphs + five list items): 200, correct canonical, exact text/order/links/classes, and no extra blank line.
  7. Existing Wiki graph/search/deep-link and linked-Person navigation, removed People-catalogue 404, cache/SEO/link checks, and public-template readability/source checks remain green.

Browser, accessibility, and screenshots

Render impact is direct, so Playwright and screenshots are applicable.

At desktop 1440x900 and mobile 390x844, independently open and capture:

  • /wiki/ai-tools-for-personal-productivity;
  • /wiki/apache-airflow;
  • /people/aleksandrkim.html;
  • /people/jacquespeeters.html.

The independent tester captures all eight PNGs below .tmp/screenshots/issue-271/ and inspects:

  • intended 200 page/canonical rather than error/debug output;
  • no blank/indented first or last visual line in paragraphs/list items;
  • source-authored wrapping/line breaks and exact visible copy;
  • Markdown link label/destination/focus/accessibility name and safe external attributes where synthetically/browser covered;
  • list markers/order, heading hierarchy/anchors, landmarks, keyboard reachability, 200% zoom/reflow, no horizontal overflow, failed request, or console error.

With JavaScript disabled at 320x800, verify at least /wiki/ai-tools-for-personal-productivity and /people/jacquespeeters.html: prose, internal links, headings, and list items remain native, visible, ordered, and keyboard reachable. JavaScript-disabled screenshots are not separately required because no script behavior changes; the browser assertions are mandatory.

Verification contract

Use uv-backed commands/Make targets only. Run the three focused Django/source modules, relevant link/SEO/content tests, and focused browser/accessibility scenarios. Then produce the final versioned verification plan and execute a fresh full profile because a shared public template affects 720 checked Wiki/Person records: locked format/lint/type/quality, full Django, full Playwright, screenshots, content/link/SEO invariants, migration drift, container, evidence validation, and report validation. Unknown graph/history also selects full.

  • Migrations: new migration execution not_applicable; migration drift required.
  • Render/screenshots: rerun, never not_applicable.
  • Generated content/#253: no generated path changes; final diff records generation/source attestation not_applicable. Any projection/source/pin/manifest/hash edit stops and returns to PM/#253.
  • Deployment: outside engineering/testing. After tester PASS and PM acceptance, engineer commits with Closes #271; orchestrator alone merges/pushes and on-call observes exact-SHA CI/deployment.

Non-goals

  • No copy/content/link rewrite, projection regeneration, source/pin/provenance/manifest/hash/adoption change, or #253 recovery.
  • No route/canonical/alias/cache/SEO/sitemap/graph/search/Person relationship change.
  • No CSS, white-space rule, typography, spacing scale, layout, responsive breakpoint, color, design, JavaScript, or visual redesign.
  • No heading/tag/ID releveling, list semantic conversion, wrapper/ARIA/focus change, or article/book/podcast prose change.
  • No public_text/Markdown grammar/sanitizer/escaping/safe-marker change and no new template tag/filter/dependency.
  • No trimming/normalizing source-authored whitespace, global {% spaceless %}, response minification, broad template refactor, snapshot rewrite, assertion weakening, or unrelated cleanup.
  • No model/migration/provider/production action, commit/push/merge/deploy during grooming or engineering.

Lifecycle

Engineer works in a fresh isolated worktree, makes the smallest template/test change, runs focused/full gates, posts a Software Engineer Report, and freezes uncommitted. A separate tester recomputes the plan/census, runs all gates, captures/reads screenshots, and posts a tester-final report. PM accepts or returns the candidate. Engineer then creates the focused Closes #271 commit; orchestrator alone merges/pushes and on-call owns CI/deployment observation.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with templates/public/_prose_body.html and the introducing commit 912f60fff9aed1dfd306fe9eba9d465674976dcc, then run the focused tests in content/tests/test_wiki_design.py, content/tests/test_person_profile.py, and content/tests/test_public_template_source.py. Verify the four prose branches preserve source whitespace, links, escaping, structure, and headings, with browser and accessibility coverage for the listed Wiki and Person routes. Done means the focused and required broader verification gates pass without changing filters, CSS, projections, or unrelated templates.

Written by the indexing model from the issue text.

Assessment

Tech stack
django, playwright, python
Domain
accessibility, frontend, testing, web-dev
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.