Skip to content

Latest commit

 

History

History
696 lines (594 loc) · 35.9 KB

File metadata and controls

696 lines (594 loc) · 35.9 KB

Divergences from the template

Every DELIBERATE difference between this repo and dash-documentation-boilerplate, with its reason. This file is the boundary between design and drift:

  • Template syncs read this file FIRST and must not "restore" anything recorded here.
  • A difference not recorded here is treated as drift and will be synced away.
  • Record the divergence in the SAME commit that creates it — one line: what differs, why, and what the template would otherwise do.
  • An empty list is a statement too: it means this repo intends to match the template exactly.

Fleet precedents for what belongs here: flexlayout's own-source _build_llms_doc dedup and app-key sourcing; flows' own _health_body payload shape (ports the healthz CONTRACT, not the template's file); clerkhook's minimal {ok, app, build} healthz and its heartbeat-as-before_request (the single anonymous 200 on a locked host); muischeduler's no-npm dependabot scope.

What this file is NOT. Every fork re-keys its identity — app key, BASE_URL, brand strings, the SATELLITE_APP_KEY fallback literal, its own doc set. That is the fork ritual, not a divergence, and it is not listed below. Nor is drift: places where this repo is simply BEHIND the template are listed in the last section, so a sync knows to close them rather than treat the gap as a decision.


Recorded conventions (not divergences)

Guard entries. Every line here documents something this repo MATCHES or deliberately does NOT carry — an absence a sync would otherwise read as drift and helpfully undo. Nothing in a diff distinguishes a deliberate absence from an accident, and a test docstring is invisible to both the fan-out and the next sync author, who read THIS FILE. Adding one costs a sentence; the alternative costs a fortnight of a defect walking back in.

  • There is no User-Agent list in this app, and there must not be (1.6.34). dash_improve_my_llms.classify() is the one classifier. This tracker carried a local list for a year: it filed ClaudeBot — Anthropic's TRAINING crawler — as search, still named the retired anthropic-ai / claude-web tokens, and counted every UA-less or library client as a person. Every host in the fleet reported those numbers. A token the registry lacks is a pushback to the package seat, never a table here. tests/test_analytics_classifier.py greps the module and goes red if one returns. The same rule now covers vendor_class (1.6.44 item 8): prefer the package's value, derive from the package's REGISTRY when absent, never from a local map.
  • Content images carry width/height and NEVER loading/decoding (1.6.44 item 6f). Neither is a prop of dash 4.4.1's html.Img and Dash RAISES on an unknown one, so adding them is a collection error on every page with an image rather than a degraded hint. tests/test_a11y_agentic.py pins the TypeError and goes red the day Dash learns the props — at which point the sub-item can be completed instead of capped.
  • The image size readers use the STDLIB only (1.6.44 item 6f). Pillow is installed here by two BUILD-TIME scripts (make_social_card, make_favicons) and is deliberately absent from requirements.txt. The template's first version of this feature imported it and was therefore inert in production and on every CI leg while looking correct in review.
  • skip is a verdict in the battery, never a pass (1.6.44 item 5). scripts/network_smoke.py answered an absent precondition with expect(True, ""). A check that passes when it swept nothing reads identically to one that swept the corpus and found it clean.
  • Assertions of the form "this string is absent from this module" parse, never grep (1.6.44 item 13). conftest.live_source() strips comments AND docstrings via ast. A comment strip is not enough: a function docstring quoting a measured result tripped the UA-list guard during item 8, because good documentation explains the absence of exactly the thing such a detect hunts for.

This repo's divergences

1. It pulls from a local sibling working tree (see the CORRECTION below)

No other fork has an upstream of its own. The private tree is ../dash-leaflet2; scripts/sync_from_rnd.py PULLS from it (dry run by default) and refuses to overwrite the files this mirror owns (MIRROR_OWNED: run.py, README.md, requirements.txt, lib/, pages/, scripts/, vendor/, Dockerfile, render.yaml). Two R&D pages are permanently withheld (DENY_DOCS), and this repo ships its own lean /tile-selector page in place of the 3,700-line R&D lab.

Consequence for a template sync: a template change to a MIRROR_OWNED file applies here normally — the guard is against the R&D pull, not against upstream. But a change to docs/ must not assume the doc set matches either repo.

CORRECTION, 2026-08-31 — "private R&D repo" was wrong. The owner challenged the story and it does not survive measurement. There is no private repository and this is not a mirror of one. Both directories carry the SAME origin, github.com/pip-install-python/dash-leaflet2; ../dash-leaflet2 is a local sibling working tree, same origin, unpushed, which scripts/sync_from_rnd.py reads BY PATH. Everything operational above stands — it is never a drop target, its contents still flow into docs/ through the sync, and its files are still not this repo's to edit.

Measured here, and it is worse than "divergent": that tree has one commit (b308393, its own root), which is not on the remote (git ls-remote finds it nowhere) and shares zero commits with this repo's 82 — the two histories are unrelated, and its local origin/main ref points at its own unpushed root, which is a fiction. Of its 106 dirty paths, 31 are UNTRACKED, and docs/text-marker/example.py is one of them.

So the examples that are phase 2's source of truth are not in any commit, not on any remote, and present only as loose files on this one machine. A git clean -fd there loses them with nothing to restore from. The phase-2 session must snapshot that tree — a pushed branch or the owner's backup — BEFORE it edits anything, and should not assume git checkout can undo a mistake there.

RETIRED, 2026-08-31 — scripts/sync_from_rnd.py is deleted. Owner's decision 0af. Everything above and in the correction is now history: no script reads that tree, so there is no pull to be destructive, nothing to mark MIRROR_OWNED against, and no DENY_DOCS to enforce.

What retired it was the divergence map. The sibling tree turned out to be a strict ANCESTOR with no new work — 26 of 27 changed .md files carried lastmod: here and not there, its docs/home/home.md was still the placeholder "This page demonstrates Home.", dl2_tiles.py and dl2_locations.py did not exist in it at all, and its package-info.json said 0.0.1 against this repo's 0.2.2. A pull would have stripped 26 sitemap dates, replaced the home page with a placeholder and reverted 15 examples to their pre-refactor state. A mechanism whose only remaining effect is to regress the site is not a mechanism worth keeping behind a warning.

What replaces it: nothing, deliberately. This repo is simply the repo. The two pages that were never meant to ship — docs/sprite-generator/ and the 3,700-line R&D docs/tile-selector/ lab — are not here and cannot arrive, because nothing copies from that tree any more; this repo keeps its own lean /tile-selector page. The sibling tree is snapshotted (9fde34f, bundle at ~/leaflet-rnd-2026-08-31.bundle) and archived.

Consequence for a template sync: the MIRROR_OWNED list is gone with the script, so the seven template surfaces hardened into it at 115f1c4 no longer need that protection — nothing can overwrite them. Template syncs are unaffected: they were never what that list guarded against.

2. It ships a component PACKAGE alongside the docs site

src/ts/ → webpack → dash_leaflet2/, published to PyPI as dash-leaflet2. The template is a site and nothing else. What this adds, all absent upstream and none of it drift: package.json / package-lock.json / node_modules, pyproject.toml (requires-python >=3.9 — the PACKAGE's floor, deliberately lower than the docs site's 3.10, which is bounded by vendored dash-clerk-auth, and lower than the container's 3.12), MANIFEST.in, usage.py (the compiled dl2.* demo on :8060, next to run.py's site on :8050), RELEASING.md, COMPATIBILITY.md, scripts/compat_matrix.py + _compat_runner.py + check_release.py, and the dash_leaflet2/metadata.json / .compat/ ignores.

3. .github/dependabot.yml adds an npm ecosystem

The inverse of muischeduler's no-npm scope, and for the same reason read the other way: this repo actually builds a JS bundle, so the build toolchain is a real dependency surface. github-actions and docker match the template.

pip is now ABSENT here as well (template 1.6.24, applied by hand 2026-08-26): on range requirements dependabot can only propose floor RAISES, and floors move through sync specs instead. So the npm block is the whole of this divergence.

THE FENCE WAS PROVEN ON THIS PATH, and it is worth recording that it was not hypothetical. SYNC-1.6.22-1.6.29's sync-verbatim block lists .github/dependabot.yml, every one of its adoption gates is satisfied here, and the F3b fan-out ran against this repo on 2026-08-26 (PR #25, merged). It copied three files and SKIPPED this one — the npm ecosystem is still here because the byte-owned block below told the machine to leave it alone. Without the fence the copy would have landed and removed the npm block silently, since removal by byte-copy looks identical to an intended removal in a diff.

4. robots.txt is OPEN to AI training

block_ai_training=False where the template ships True (run.py, with the full rationale inline). This is not a stale setting: dash-improve-my-llms 2.3.3 made True safe, and the position was re-reviewed on 2026-08-23 (the ≥2.7.1 floor round) and KEPT. The reason is what this host is — documentation for an MIT-licensed component library, whose distribution channel is a model recommending dash-leaflet2 to a developer who will never visit the site. Blocking training would trade that away for a licence term the MIT licence does not contain. A host with proprietary content should decide the other way; scripts/network_smoke.py encodes the split so verification reads this as a stated position rather than a miss.

This is the FLEET'S RECORDED EXCEPTION to the network-standard block_ai_training=True, and it is visible from outside: as of 2026-08-26 robots.txt serves zero Disallow lines and names no GPTBot / ClaudeBot / CCBot group, and both GPTBot/1.2 and ClaudeBot/1.0 fetch / with a 200. A wire check that reads those as a miss is reading this divergence.

disallowed_paths=[] matches the template's value today, but for a reason worth keeping written down: Disallow: /admin/ used to be here and was working against itself — robots.txt is public, so the line advertised the admin path while providing no access control. What protects that surface is auth (the board gates twice and fails CLOSED); what keeps it out of the index is mark_hidden("/admin/control-board").

5. /healthz carries three fields the template's does not

version, base_url, reporting, on top of the fleet-standard ok / app / backend / dash_version / build / geo / python. All three predate the template's health work and are kept because each answers a question the standard payload cannot:

  • base_url — the origin this satellite ADVERTISES, checkable from outside. A host that resolved BASE_URL to localhost looks healthy on every other signal while every canonical link it publishes is dead. (require_owned_base_url refuses the worst cases at boot; this covers the rest.)
  • reporting — whether the traffic reporter could actually POST, so "wired but secretless" is visible without reading boot logs.
  • version — APP_VERSION, distinct from build: which RELEASE, not which commit.

A wire check against this host should expect a superset, never a mismatch. The fleet contract is that app, build and geo mean what they mean everywhere.

6. lib/health.py registers /healthz on all THREE backends

The template serves the FastAPI build's /healthz from lib/asgi_routes.py (a typed route, so it appears in Swagger). This repo has no lib/asgi_routes.py, so the route is registered from lib/health.py for flask / fastapi / quart alike. Same health_payload builder, same contract — different mounting point. A sync must not port the asgi_routes half without porting the module.

7. _build_llms_doc is handed the PUBLISHED name, not metadata.name

pages/markdown.py calls _build_llms_doc(published_name(metadata.endpoint, metadata.name), ...) where the template passes metadata.name. Without it the home page served THREE <h1>s to a crawler: its preamble said "Home" (the nav label) where dash-improve-my-llms injects the site brand, so the 2.7.0 prerender dedup had two different strings and could not fire. lib/page_visibility.published_name substitutes SITE_BRAND at "/" only; the nav keeps "Home". Same class as flexlayout's own-source _build_llms_doc dedup, and the template should probably take it.

8. _access.configure(force=True) is unconditional

The template forces the wiring only when a gate env var is present. This repo is the fleet's gate PILOT and wires the verdict path unconditionally, so that path — and the prerender's use of it — has been running in production, answering allow, since before PAGE_DEFAULT_TIER flipped. The env flip was the whole change, and flipping it back is the rollback. A sync that restores the conditional form would silently un-wire the pilot on any host that later clears the env.

9. The gate card does not promise an AI assistant

lib/gate_layouts.py drops "and the AI assistant" from the preview copy the template ships. This host does not wire one, and a sign-in card that names a feature the site does not have is a broken promise at exactly the moment it is asking for an account. (Template-class: the template does not wire one either — see Findings.)

10. BASE_URL reads two env names

APP_BASE_URL first (network-standard), then DASH_LEAFLET2_BASE_URL (this repo's legacy spelling). An ALIAS, never a rename: render.yaml sets the legacy name on a live service, and removing one of two env names from a running host is how it starts advertising the wrong canonical origin — which deindexes it silently. The template reads APP_BASE_URL alone.

11. .claude/CLAUDE.md carries the contract, not a second project overview

The kit's contract and traps sections are BYTE-IDENTICAL to the template's (verified by md5 of the tail from ## Network role & the behavioral contract onward). The sections ABOVE that are this repo's, because unlike the template this repo has a root CLAUDE.md that is already its project overview — the Leaflet-2 API traps, the two deliverables, the build pipeline, the mirror relationship. Porting the template's top half verbatim would have installed a second, contradicting overview titled "Dash Documentation Boilerplate" into every session here, describing a project this is not and a /directives page this does not have. The file's own first paragraph is the licence for this: identity derives from the repo, never from this file.

Sync rule: changes to the contract or traps sections are verbatim targets. Changes to the template's project-overview sections are not-applicable here by construction.

12. .gitignore keeps this repo's own state files

dash_leaflet2/metadata.json (27 MB react-docgen artifact), satellite_traffic.jsonl (the RETIRED Gen-1 ledger, kept ignored so a stale local copy can never be committed), .compat/, and flask_session/. The .claude allow-list and the session-document block match the template exactly.


13. The site CI matrix's legs are the FLOOR and the adjacent minor

The template's Python window rolls: its two include legs are X.Y-1 and X.Y-2 around the fleet Python. This repo's are 3.10 and 3.13 against a 3.14 image — the second is the template's shape, the first is not.

3.10 is a real floor, not a preference: python-frontmatter 1.3 imports typing.TypeGuard, and the vendored dash-clerk-auth 1.0.5 declares requires-python >=3.10. A rolling window would stop exercising that floor the moment the fleet Python moved twice — which is precisely when a floor breaks quietly, because nothing else in the tree resolves requirements.txt at its lower bound.

So tests/test_python_version.py here asserts what this fork actually means — one leg IS the declared floor, one leg is directly below the fleet Python — instead of the template's "within three of the fleet minor". Both halves are still pinned, so the window can neither collapse to one nor drift. The floor itself is stated ONCE, as lib.constants.DOCS_PYTHON_FLOOR, and the test reads it from there rather than repeating the literal.

Not to be confused with the PACKAGE lane. Package · Python runs 3.9–3.13, testing the dash_leaflet2 wheel's own requires-python >=3.9 against a bare pip install dash. That window is the package's business and is deliberately wider than the site's; test_the_package_lane_is_out_of_scope_and_stays_wide fails if anyone ever "aligns" it with the image. Two Pythons in one ci.yml is the shape spec 1.6.28 describes, and this is that shape.

14. scripts/smoke_live.py asserts this host's open-training posture

The template's copy has no equivalent: divergence 4 is this fork's, so the check that proves it on a live host is too. The block asserts robots.txt serves no ClaudeBot stanza and fingerprints the crawler rules the running artifact produces.

This is why item 6 of SYNC-1.6.22-1.6.29 was ported here as CONTRACT — the wake loop, the retry knobs and the SSL context, added to this fork's file — rather than by the byte-copy the spec recommends. A byte-copy would have deleted the assertion, and a deleted check does not fail; it just stops being true without telling anyone. The spec anticipates exactly this ("a fork that replaced the tool records the divergence and ports the contract half"); the record is this entry.

15. /api reads a COMMITTED props extract — RETIRED at template 1.6.41

Kept as history because the retirement is the useful part. This fork found that lib/api_reference.load_package reads the component package's metadata.json, which here is a 27 MB react-docgen BUILD artifact that .gitignore excludes and MANIFEST.in excludes from the wheel. On Render it therefore does not exist, and upstream's code returned [] SILENTLY — /api would have shipped empty at 200, with a canonical and an h1, while every local check passed because locally the file is there.

scripts/build_api_metadata.py distils it to dash_leaflet2/api_metadata.json (26 components, 302 props, 78 KB), which IS committed and carries the generated stamp that is /api's sitemap lastmod.

Template 1.6.41 adopted the whole road — same SLIM_METADATA constant, same generated stamp, same resolution order — and added a third fallback to the classes' docstrings. lib/api_reference.py is byte-identical to template 4ac02e0 again, so this is no longer a divergence. What REMAINS this fork's is scripts/build_api_metadata.py (the template has its own) and the fact that metadata.json is gitignored here at all, which is what makes the extract road load-bearing rather than decorative — pinned by test_this_repo_really_has_no_committed_metadata_json.

16. Two generated pages register their full machine record — RETIRED at 1.6.41

Also kept as history. /changelog and /api arrived from 1.6.38 leaving a module-level LLMS_DOC for the package to discover, which publishes the prose but sends no lastmod, so both entered the sitemap dateless — invisible upstream, where no test checks it, and red here under tests/test_seo_icons.py. /changelog additionally needed its date parser widened: this repo's CHANGELOG has always used an EM DASH, and the template's regex took only an ASCII hyphen, so it matched every version and dropped every date.

Template 1.6.41 took both findings and went further: the pages now call page_visibility.register_default + page_tiers.register + register_page_metadata(..., lastmod=...), which also brings them under the control board's llms.txt toggle; and the heading regex generalised to the seven shapes the fleet writes, crediting this repo's em-dash case. Both files are byte-identical to 4ac02e0, so neither is a divergence any more.

17. HeadAsGetMiddleware is KEPT, where the template retired it

Template 1.6.44 item 2 removes the HEAD->GET ASGI shim it added at 1.6.32. Its own note is explicit that the retirement is "gated on the pin (item 1) and not on the calendar": at dash-improve-my-llms 2.9.4 the package walks the router and adds HEAD wherever GET is allowed, including Dash's lifespan-registered page catch-all — the one case the middleware was kept for.

This fork does not take that pin. Rider 1 of the 1.6.44 drop leaves our requirements line at dash-improve-my-llms[flask]>=2.8.0 until the fleet pin lands at 1.6.45, so the package-side fix is not present here and the shim is still load-bearing. Measured in-process on the FastAPI lane at the resolved 2.8.0, 5 paths x 3 UAs, BEFORE porting: four of fifteen pairs mismatched — /healthz 405 on all three UAs, and / 405 to a browser while returning 200 to a crawler and to curl (the prerender middleware answers above routing, which is the kit's own trap). The package's routes — /llms.txt, /robots.txt, /sitemap.xml — already passed: 2.7.2 fixed those. The residue is exactly the two routes the package does not own.

Note this fork had NEVER carried the class; grep -rn HeadAsGet returned zero lines. So this is not "declining to retire", it is porting for the first time, at the moment the template drops it — and absent-vs-retired is the confusion the ops seat flagged from pannellum. Production is Flask, where Werkzeug derives HEAD from every GET rule, so the wire was never affected; lib/backend.py puts the ASGI lane one env var away, which is what makes it worth fixing.

TWO VERSION REGIMES, ONE POSTURE — and the distinction is what a >= floor makes unavoidable. The floor permits any version from 2.8.0 up, and a fresh venv resolves 2.10.0 today, so both of these are normal states of this repo:

  • below 2.9.4 — the package does not walk the router, the shim is LOAD-BEARING, and its absence is a defect;
  • at or above 2.9.4 — the package adds HEAD wherever GET is allowed, so the shim is REDUNDANT. Not wrong, not broken: the parity table is 15/15 either way. It stays INSTALLED.

The shim is retired by the PIN ROUND (1.6.45), never by whichever version a resolver happened to pick, and never on a date — a re-measured parity table without it is the trigger, the same rule as run.py's _openapi_kwargs guard.

tests/test_head_get_parity.py records which regime it ran in and xfails the "shim still required" clause above 2.9.4, so the day the package takes over is VISIBLE in CI output as an XFAIL transition without being a red build. It asserted that clause as a hard failure until the ops seat mirrored this tree on a FRESH venv: our >=2.8.0 floor resolved 2.10.0 and the guard failed on every leg. A reminder that turns a permitted dependency resolution into a red build is not a reminder.


18. Item 6's recorded sub-items

The loading/decoding prohibition and the stdlib-only size readers are guard entries and live under Recorded conventions above. The two below are this fork's own findings and belong here.

(d) The mobile console error is NOT CLEARED on this host. The template records it as "not reproduced" against its own deployed build and adds, correctly, that a fork which DOES see it must not read that line as clearance. This repo is one of the three hosts the symptom was originally reported on (the template names leaflet, llms and pannellum), so its own measurement does not transfer here and this fork must not inherit the clearance.

MEASURED HERE, 2026-09-05, in the owner's Chrome against the deployed build 2aea641 (which predates this pass):

/                desktop     4 console messages, ALL `LOG`, 0 errors
/pointer-events  390x844     15 console messages, ALL `LOG`, 0 errors

Every line is Clerk's ([Clerk Glass] Theme changed, [Clerk] Session changed, the avatar element check) plus the satellite auth handshake. No error, no warning, on either page.

TWO HONEST LIMITS on that reading, because a clean measurement taken the wrong way is how a fork clears a defect it still has. The session was SIGNED IN, so the anonymous path is not covered; and the window was resized to 390x844 but the captured screenshot still rendered the desktop layout, so this is a narrow window rather than a confirmed phone viewport. Status: NOT REPRODUCED on this build, NOT CLEARED — re-measure signed-out, in a real device emulation, before closing it.

(e) Shipped CSS/JS are not minified, deliberately. Same posture as the template and for the same reasons, re-checked here rather than inherited: assets/ is text served over a gzip transfer encoding, and a build step would trade the readable stylesheet for a saving the encoding has already taken. This repo has a stronger version of the template's argument — assets/leaflet2_maps.js and assets/style.css ARE the documentation for how the showcase wires Leaflet 2, and a reader who opens them expects the source a human wrote. Revisit if assets/ grows past a few hundred KB.


Byte-owned paths

Paths this fork owns byte-for-byte. The F3b fan-out never overwrites a path listed here; everything else in the spec's sync-verbatim block is the template's to update mechanically. Prose above explains divergences; this block is the machine answer.

Repo-relative paths, one per line, # comments, no ..; exactly one block. An EMPTY block means "the template owns every sync-verbatim path here" — present so the absence is a statement. When the block exists it is authoritative; a fork without it gets the conservative mention heuristic (over-flags, never restores).

Audited 2026-08-26 against the union of the three live specs' sync-verbatim blocks: the three kit skills, tests/test_claude_kit.py, .github/dependabot.yml, tests/test_auth_demos.py. Exactly one of them carries a byte-level claim in the prose above — divergence 3. The kit skills and the kit test are TEMPLATE-owned here and byte-identical to template HEAD (md5-verified this pass); this fork claims nothing on them, so they stay out and the fan-out keeps them current. Two nearby mentions are deliberately NOT entries: the fleet precedent "muischeduler's no-npm dependabot scope" describes another fork's file, and the /new-component skill named in the drift section lives in the sibling working tree's .claude/rules/, not in the kit's skills/.

The cost of the one entry, written down so nobody rediscovers it: a listed path is one the fan-out will never update either. Dependabot changes therefore land here BY HAND. The first such change has now been made: template 1.6.24's pip-ecosystem removal, applied 2026-08-26 as an edit that keeps the npm block rather than the whole-file byte-copy the spec's block would have performed. That is the standing cost of this entry, and the standing procedure for it — read the template's copy, port the intent, keep divergence 3.

Re-audited 2026-08-26 against SYNC-1.6.22-1.6.29, which added .github/dependabot.yml and tests/test_auth_demos.py to the verbatim block. The audit's answer is unchanged: one entry. tests/test_auth_demos.py arrived from the fan-out and is template-owned here — this fork claims nothing on its bytes. scripts/smoke_live.py rode the block for exactly one round (1.6.28) and was pulled back out at 1.6.29; had it still been cargo it would have needed an entry, because this fork's copy carries a check the template's does not (the open-training assertion, divergence 4). It is contract-class now, so the fence stays at one path — but the near miss is the reason to re-run this audit every round rather than trusting the last one.

Re-audited 2026-08-29 for SYNC-1.6.22-1.6.35 (items 12 + 13), whose block adds tests/test_analytics_classifier.py and tests/test_traffic_rollup_v4.py. Both arrived here as byte copies of template HEAD and this fork claims nothing on their bytes, so the answer is unchanged: one entry. Item 12's other two tests (test_read_ledger.py, test_traffic_page.py) are contract-class upstream and were ported, not copied — they call this tree's conftest fixtures and pages/control_board.py. Item 13's tests/test_cd_promotes_release.py is likewise a port: it parses THIS fork's cd.yml, whose host string, wait sizing and comments are its own.

# Divergence 3: this repo builds a JS bundle, so its dependabot config
# ADDS an npm ecosystem the template's copy does not have. The 1.6.24
# rewrite is a whole-file byte-copy that would silently remove it.
- .github/dependabot.yml
# PORTED, not copied, at item 18 — each carries fork content a byte-copy
# would delete. Fenced in the same commit that ported them, per the rule
# that a path you ported belongs here before the next reclass, not after.
#
#   components/header.py    the satellite mark, the green wordmark, the
#                           `Leaflet <version>` subline, the mobile glyph,
#                           and the CARTO tile-theme bridge callback the
#                           showcase maps depend on (no template equivalent)
#   components/navbar.py    `nav_label()` — this fork reads `nav:` for the
#                           rail; otherwise template-shaped
#   components/appshell.py  the desktop-navbar collapse store + its two
#                           clientside callbacks, absent upstream
#   pages/markdown.py       this repo's directive set, `.. source::`
#                           expansion, and the published-name llms_doc call
#                           (divergence 7)
#   lib/constants.py        this site's identity, DOCS_PYTHON_FLOOR, the
#                           two-env BASE_URL alias (divergence 10)
- components/header.py
- components/navbar.py
- components/appshell.py
- pages/markdown.py
- lib/constants.py

Declared posture

What this host SERVES, measured — not what the template ships. The hub used to keep this as a seeded table it could not re-measure; the fence (template 1.6.30, item 9) homes each posture in the repo that serves it. tests/test_claude_kit.py validates the SHAPE and holds runtime against render.yaml; no test can tell a stale 200 from a fresh one, so the values below carry the date they were taken.

  • ai_bots — re-measured 2026-08-30T14:25Z against https://leaflet.2plot.dev for BOTH vendor UAs the fleet checks: ClaudeBot/1.0 and GPTBot/1.2 each answer 200 on /, /llms.txt and /healthz (six lines, all 200); robots.txt serves zero Disallow lines and names no training UA at all. That is divergence 4 on the wire, not a miss: this host ships block_ai_training=False because it is documentation for an MIT-licensed component library whose distribution channel is a model recommending dash-leaflet2.

    As of template 1.6.37 this is no longer a divergence in POSTURE — the fleet default flipped to allow, and the tool became per-vendor policy rather than per-class blocking. What remains recorded here is that this host got there first and has the wire history to show it. IN-PROCESS AND WIRE AGREE, all six: the app answers 200 itself, so there is no edge wall in front of this host either (the template's drop assumed a Cloudflare rule; the owner has since confirmed the feature is Enterprise-only on this plan and no zone rule exists).

  • healthz: full — and a superset at that; see divergence 5, which adds version, base_url and reporting to the fleet-standard payload.

  • runtime: docker — render.yaml builds the Dockerfile, so PYTHON_VERSION is deliberately ABSENT there (spec 1.6.28 item 5: on a Docker service nothing reads it, and a string that reads like the platform's setting and can never be true is its own defect).

  • deploy: release-branch — since 2026-08-29 (template 1.6.35, item 13). Render watches release; only cd.yml's deploy job writes it, fast-forward, after the CI matrix is green. main ahead of release is an uncertified push pending, never drift. An absent deploy: key would read as "this host still watches main".

ai_bots: {"/": 200, "/llms.txt": 200, "/healthz": 200, "/robots.txt": 200, "/sitemap.xml": 200}
healthz: full
runtime: docker
deploy: release-branch

Where this repo is BEHIND the template (drift, not design)

Listed so a sync closes them rather than reading the gap as a decision. None of these are divergences.

  • lib/health.py::_resolved_country() reads the Flask request context only. The template takes headers per route after the pannellum finding (its FastAPI healthz answered "no request context" forever). Production here is DASH_BACKEND=flask, so the live payload is correct — but the fastapi and quart lanes this repo registers would report the same false negative.
  • components/appshell.py hardcodes 70 where the template uses HEADER_HEIGHT from lib/constants.py.
  • Missing template test modules: test_auth_wiring.py, test_config.py, test_control_board.py, test_css_hygiene.py, test_docs_content.py, test_llms_routes.py, test_network_directory.py, test_proxy_scheme.py, test_runtime_imports.py. (test_excluded_links_hidden.py and test_nav_contract.py arrived with the 1.6.38 navigation contract; lib/directives/headings.py came with it too, so both are off this list. test_admin_nav.py was REWRITTEN against the new admin mechanism rather than retired — see below.) (This repo has five the template does not: test_admin_nav.py, test_healthz_identity.py, test_network_surfaces.py, test_page_structure.py, test_page_visibility_reload.py — test_page_structure.py is the template's test_pages.py under this repo's name.)
  • scripts/audit_links.py and scripts/dev.sh are not ported.
  • The Dockerfile has no HEALTHCHECK, and CMD binds a bare ${PORT} rather than ${PORT:-8050} (SYNC-1.6.10-1.6.16 item 5). An empty PORT collapses the bind. This also makes SYNC-1.6.17-1.6.21 item 2 UNSATISFIABLE until it is fixed: that item asks CI to assert {{.State.Health.Status}}, which is none on an image that declares no healthcheck — so ci.yml still polls {{.State.Running}}. Item 5 has to land first; the two are one change in practice.
  • cd.yml's build-match wait is under-sized in four ways (SYNC-1.6.10-1.6.16 item 4). It does compare build == GITHUB_SHA against the body — the part that matters, and it certified the last two deploys — but: the loop is 60 x 15s where the spec wants >=100; timeout-minutes: 20 where it wants >=30; a hookless deploy emits ::notice:: where it wants ::warning::; and the verify job's if excludes 'cancelled' but not 'skipped' (muicharts' guard). A floor bump busts the pip cache, so the round with the most to prove is the one whose deploy is slowest — dash-email timed out on exactly this class.
  • kickoff/ is missing from .gitignore. The template's session-document block carries it (its .gitignore line 169, in a separate block above the *-*.md patterns, which is how this list read as complete once before — batch-1's correction). Probed 2026-08-26: kickoff/probe.md is committable in this repo today. Nothing is at risk right now because no kickoff/ exists here.
  • The root CLAUDE.md advertises .claude/rules/ and a /new-component skill that no clone of this repo has ever had. They exist in the sibling working tree; the blanket .claude/ ignore (removed 2026-08-24) is why they never arrived. Either port them through the mirror or stop advertising them.