selfdoc v0.37.1 /Changelog
On this page

#Changelog

#0.37.1

Repairs the unified-site deploy: mounted projects address their posts where the site serves them and stop shipping an unreachable 404, generated reference pages mark extracted code as code and renest doc-comment headings, and the wheel declares its dependency floors.

#Fixes

  • [selfdoc] Declared dependency floors. The published wheel now requires selfdoc-core>=0.9.0 and strictcli>=0.36.0. The 0.37.0 wheel declared a bare selfdoc-core, so a fresh install could resolve a pre-0.9.0 engine that no longer carries the symbols selfdoc imports, and fail at startup.
  • [selfdoc] A mounted project's posts are addressed where the site serves them. A project declaring topology.docs_base and topology.slug is served under its slug, but the assembly serves every project's posts from one shared blog/ at the site root. The build addressed them under the slug anyway, so a post's canonical, its sitemap and feed entries, and every project-page link to it named an address the site does not serve. A project with no mount keeps a self-contained blog, and a posts-only build now renders a post byte-identically to the full build.
  • [selfdoc] A mounted build writes no 404 page. A hosting provider answers an unmatched address from the root of what it serves, so a 404.html buried under a project's slug is never reached. Every mounted project shipped one anyway, and it was an unreachable page that still had to satisfy every assertion made about a page -- it had no canonical, and that blocked the whole site's deploy. A standalone project, whose output root is the served root, still writes its own.
  • [selfdoc] An acronym-prefixed identifier is no longer reported as a misspelling. SPELL001 skips a name with an internal case change, but it recognized only a capital following a lowercase letter -- so TextResponse passed while JSONResponse and HTMLResponse were flagged. A run of capitals meeting a capitalized word is now recognized too.
  • [selfdoc] A code span written with two or more backticks renders as one span. The RST-style x form -- also the form CommonMark requires when the code itself contains a backtick -- was split on single backticks and came out as two empty <code> pairs with the text loose between them. The renderer now shares the pattern the directive scanner and the spell mask already used.
  • [selfdoc] Generated reference pages say which of their text came from code, and a doc comment's headings no longer break the page. Symbol names, module paths, Go flag types and defaults, and SQL object and type labels were emitted as plain prose, so the spell checker read them as English and reported identifiers like JSONResponse and returncode as misspellings an author had no way to fix; they are code spans now, in headings and inline alike. Separately, Go's doc convention writes section titles as # Usage and any language's doc comment can carry markdown headings -- emitted verbatim onto a reference page they became a second H1, which the build refuses, so one such package was enough to stop a project's docs build. Every extractor now renests extracted headings beneath the heading they were emitted under. Docstring prose is unchanged: an identifier written into a sentence is still the author's to backtick.

#0.37.0

check lints posts, spell-checks prose and resolves every link the build emits; init requires the base URL and the author; lint suppression is validated against the registry and reaches warnings only.

Context

One program across all three packages: the site's addressing, the assembly that serves it, and the authoring checks that keep it honest.

Addressing changed first. A page's current version now lives at a stable, unversioned address and superseded versions are archives under v/, declared as such in canonicals, sitemaps and feeds. Posts moved to one site-level blog/ namespace shared by every project. Every link, canonical and sitemap entry the build emits is now resolved against the files it actually wrote (LINK001), and the assembly worker carries a redirect map generated from the manifests, so the retired address schemes still land somewhere real in a single hop.

The assembly stopped being CI shell. The deploy body is a command (assembly integrate), the workflow that calls it is a thin generated artifact whose tool pins are rewritten and checked against PyPI on every sync, and the assembled tree is verified before anything is pushed. Membership is a declared roster with a required home project served at the site root, documentation can be published without a release, and a project can be retired.

The authoring checks grew a spine: every emittable lint code is declared in a registry with its severity, suppression is validated against that registry and can no longer silence an error, posts are linted like pages, and prose is spell-checked -- including the prose a directive renders out of an authored document.

Search switched to Pagefind and the builtin engine was deleted, so search_engine is required and names the one valid value. The author block is required too: one Person is built from it rather than an Organization invented from a directory name.

Breaking changes are minor bumps under the pre-1.0 convention; each is a breaking-type entry in the changelog.

#Breaking

  • [selfdoc] **selfdoc init requires --base-url.** The site's own address cannot be inferred and every canonical link, sitemap entry and feed URL is built from it, so init now takes it explicitly.
  • [selfdoc] Lint severities come from the registry. selfdoc.check.LintResult is the registry's type: it takes no severity argument and refuses a code that is not declared in selfdoc-core's lint registry.
  • [selfdoc] SEO007 holds every page type to one word band. Generated cli-* pages were exempt from the paragraph-length rule entirely; they are now checked like every other page, so a project with generated CLI reference pages will see new SEO007 warnings until their lead-in paragraphs reach 30 words. Only the structural suppressions remain (a heading or short lead-in followed by a directive that supplies the content). The message also names the 30-80 band the rule enforces instead of an unrelated 40-60.
  • [selfdoc] **selfdoc check --ignore refuses a code the lint registry does not carry.** A mistyped code silently suppressed nothing; the flag is now validated against the registry before the check runs, and an unknown code aborts with the list of known codes.
  • [selfdoc] **selfdoc check --ignore and lint_ignore refuse error-severity lint codes.** Suppression reaches warning-severity codes only: naming an error code is a hard error that names the code and its severity, at config load and at flag parse, and the run stops before any checking happens.
  • [selfdoc] **selfdoc init requires --author-name and --author-url.** The scaffolded config declares the author alongside the base URL, because nothing about a person is inferable from a directory.
  • [selfdoc] **Pagefind is the search engine, and search_engine must declare it.** The key is required in selfdoc.json with no default and one valid value, "pagefind"; a config that omits it is refused at load, naming the key. The builtin, Fuse.js and MiniSearch engines are gone, along with the search-index.json file, the search.js bundle and the CDN script tags -- the build runs Pagefind over the finished HTML instead, and Pagefind writes both the index and the search UI into pagefind/. Install it with uv add 'pagefind[bin]'; SEARCH001 now reports a missing indexer on every project.

#Features

  • [selfdoc] **selfdoc init works on projects with no code, and emits a config that builds.** Language detection failing is no longer a refusal: a codeless project gets a config with no source key and a starter page with no extraction directive. Every field load and build require -- base URL, versions, locales -- is written into the file, so the generated configuration needs no hand-editing.
  • [selfdoc] The lint-rule table documents every rule. The Check Guide's table listed only the SEO, staleness, CLI, example and version rules; it is now generated from the lint registry and covers all 42 codes, including the description-quality, cross-reference, parameter, post and unified-site rules.
  • [selfdoc] The blog guide covers publishing without a release, the assembly roster, and retiring a project. New sections explain what a build owns and therefore what it may remove, how roster.toml declares membership, and how a project is unpublished.
  • [selfdoc] **selfdoc init no longer writes the retired indexed key, and selfdoc check runs the new LINK001 resolution check over the built site.** An emitted link, canonical, sitemap entry or feed link that names a file the build did not write is a check error.
  • [selfdoc] The blog guide documents the verification every assembly deploy has to pass. What each assertion checks, what a failure means, how to run the same assertions by hand against a checkout, and how outbound link checking is declared.
  • [selfdoc] **selfdoc check reports the post directive declaration.** A post with no directives declaration is POST006, and a post declaring false that carries a marker is POST007, naming the marker and the line of the post file it sits on.
  • [selfdoc] **selfdoc check lints blog posts.** No path linted them before: check never injected posts into the docs tree, and the build lint pass runs after the injected files are removed, so a post could carry any defect and both surfaces reported nothing. Posts are now held to every page rule, reported at the post file own path and its own line numbers. Drafts and the generated listing page are excluded, matching what the build publishes.
  • [selfdoc] **selfdoc check spell-checks documentation pages and posts.** Every page and every published post is now read for misspellings and reports them as SPELL001 errors naming the file, line and column. Genuine terms -- project names, tool names, technical vocabulary -- go on a shared accept list at ~/Projects/ark/spelling-accept.txt; a missing list simply accepts nothing yet. The new selfdoc spell-corpus command runs the same engine, read-only, over every selfdoc project sitting beside this one, so one sweep surfaces the vocabulary a whole machine uses.
  • [selfdoc] Search ships on every page, with seven filters. The Pagefind dialog opens on Cmd/Ctrl+K from any generated page -- project docs, posts, the blog index, the project listing and the site 404 -- and loads its assets from the index the build wrote, never a CDN. Every page carries filter attributes for version, locale, nav group, page type, deploy target, project and tags, so the dialog offers each as a filter group; tags are emitted one per value, so a page with several appears under all of them.
  • [selfdoc] **The directives reference documents the cv directive**, including the shape of the CV document and every field it validates.
  • [selfdoc] Content a directive renders is spell-checked. A page whose body is a directive -- the CV, the curated project listing -- had none of the text a reader sees scanned, because the source file holds a marker rather than the prose. Live CVs shipped with misspellings nothing reported. Findings now name the document the word was written in (docs/cv.toml, docs/projects.toml) with its line and column there, plus the page it renders into. Names extracted from source code are not reported -- an identifier is not prose.

#Fixes

  • [selfdoc] Assembly deploy documentation matches the deploy. The blog guide now describes selfblog assembly integrate (the deploy body) and selfblog assembly sync-workflow (which regenerates the deployed workflow and its selfblog pin) instead of the retired embedded-shell workflow.
  • [selfdoc] SEO008 stops counting version strings and years as statistics. The numeric-density rule counted any token with a digit, so a page whose only numbers were 0.36.0 and 2026 looked data-rich and the rule stayed silent. Version-shaped tokens (0.36.0, v2, 1.0.0-alpha.1) and bare calendar years no longer count; genuine quantities (42, 3.5, 87%) still do.
  • [selfdoc] Generated index pages and the documentation corpus link pages at the address the site serves them from. The auto-generated API and CLI reference indexes still linked siblings as <page>.html, the form pages took before directory-index URLs, so every link off those two pages resolved inside the index page's own directory and 404'd.
  • [selfdoc] The blog guide gives the address a post is actually served at. It documented /<project-slug>/posts/<post-slug>/, which no build has emitted since posts became site-level: a post is at /blog/<post-slug>/, under no project slug, in one slug namespace every project on the assembled site shares. The published-file record example was stale in the same way.
  • [selfdoc] **selfdoc build prints a message instead of a traceback when the config is invalid.** A present-but-unusable selfdoc.json -- no versions, no locales -- ended the process on an uncaught error; it now reports the problem and exits 1.
  • [selfdoc] SEO doc: no SearchAction. The structured-data list says the homepage WebSite node carries no SearchAction and why.
  • [selfdoc] The documentation site is canonical on the apex. Every other hostname the site is bound to 301s onto it, and the assembly guide documents the routing and the four machine-readable files at the site root.
  • [selfdoc] The configuration reference states the search engine rule. Its table described the three deleted engines and marked search_engine optional.
  • [selfdoc] The contrast check measures the stylesheet the site actually ships. selfdoc check read a packaged copy of the theme CSS that had drifted 58 lines behind the one the build emits, so SEO012 scored colors no page used. It now reads the emitted stylesheet, and the stale copy is gone from the wheel.
  • [selfdoc] **A bad config or an unknown directive stops selfdoc check with a message, not a traceback.** check read selfdoc.json with no handler, so a file build refused cleanly crashed it -- as did a suppression list naming an unknown or unsuppressable lint code, and a page carrying a directive nothing answers. All of them now print one Error: line and exit 1.

#0.36.0

Generated CLI reference pages carry effects metadata, and selfdoc check executes validate-marked examples instead of merely parsing them.

Context

Two gaps closed in the same visit.

Generated cli-*.md pages threw away every effects field the strictcli schema carries. A reader of the published docs could not learn that a command was consequential, what it would refuse to do under --dry-run, or which dangerous steps it had authored grant reasons for -- the first notice was a CI job dying on a confirmation prompt. The pages now render the effect classification, the consequential badge with --approve-consequential, the dry-run-unsupported reason and the grants table, and the index gains global flags, the reserved framework-flag quartet, infrastructure and deprecated sections.

Separately, code blocks in documentation were only ever parsed for syntax, so an example that parsed but could not run shipped looking correct. A block marked validate is now assembled and executed through the validator its language declares under the new examples config key; a failing run is EXAMPLE002 with the validator's output, and a validate marker with no configured command is EXAMPLE003 rather than a silent skip. Unmarked blocks are unaffected and are never executed.

Also: schemas/check-output.schema.json now enumerates every lint code selfdoc check emits and declares the two coverage fields it was omitting, so consumers validating or generating types from the JSON output stop losing data.

#Features

  • [selfdoc] Effects metadata in CLI reference pages. Generated cli-*.md pages now show each command's strictcli effect classification, a consequential badge naming --approve-consequential, a dry-run-unsupported note with its reason, and a Grants table. The CLI index gains Global flags, Framework flags (the reserved --dry-run/--approve-consequential/--quiet/--verbose quartet), Infrastructure and Deprecated sections.
  • [selfdoc] Examples are executed, not just parsed (EXAMPLE002/EXAMPLE003). selfdoc check now runs every code block marked validate through the validator configured for its language under examples, reporting a failing exit as EXAMPLE002 with the validator's output tail. A marker with no configured command is EXAMPLE003 rather than a silent skip. Unmarked blocks keep the syntax-only EXAMPLE001 behavior and are never executed.
  • [selfdoc] Documented the portfolio canonical. The blog guide gains a section explaining why an assembly's portfolio page names the apex rather than topology.docs_base as its canonical, and how assembly.portfolio_canonical reaches the generated deploy workflow.

#Fixes

  • [selfdoc] JSON output schema accepts all current lint codes. schemas/check-output.schema.json enumerated only the SEO and STALE001 codes, so consumers validating selfdoc check --format json rejected output containing EXAMPLE, DQ, PARAM001, RETURN001, DRIFT001 and other current codes. The enum now covers every emitted code.
  • [selfdoc] JSON output schema declares all coverage fields. schemas/check-output.schema.json omitted the documented and documented_symbols fields that selfdoc check --format json emits, so consumers generating types from the schema silently lost both.

#0.35.0

selfdoc adopts strictcli's effects regime: every command is classified, --dry-run previews instead of executing, deploy asks before it runs, and --yes is replaced by --approve-consequential

Context

selfdoc's dry-run used to be a per-command courtesy: some commands honoured it, some quietly ignored it, and nothing in the code stopped a handler from shelling out anyway. This release moves that guarantee into the framework. Every command now declares whether it is read-only or mutating, and every subprocess launch and filesystem write goes through a single chokepoint, so --dry-run records a numbered would-do log instead of performing the work -- a dry deploy names the Cloudflare project it would push to rather than pushing to it.

The confirmation story changed with it, and it is worth explaining why the flag is new. strictcli originally inferred "prompt the user" from "this command mutates something", which turned out to catch about two thirds of every CLI in the fleet -- including commands nobody wants to confirm. A prompt that fires on two thirds of invocations trains people to dismiss it, which is exactly the reflex a confirmation is supposed to prevent. So consequence is now declared per command rather than inferred, and only the commands that earn it prompt. In selfdoc that is exactly one: deploy, the only command whose effects leave the machine and land on a live public site.

--yes is gone rather than deprecated. yes is now a banned flag name framework-wide, so selfdoc <cmd> --yes is a hard "unknown flag" error instead of a silent no-op. The replacement is --approve-consequential, deliberately unwieldy so it cannot decay into muscle memory the way -y did, and self-documenting wherever it appears in a script. The practical migration is smaller than it sounds: the mutating commands that used to demand --yes from every non-interactive caller now need no flag at all.

#Breaking

  • [selfdoc] **Every command is classified, and --dry-run now previews instead of executing.** selfdoc rides strictcli's effects regime: each command declares itself read_only or mutating, and every subprocess launch and filesystem write routes through a single effects chokepoint. selfdoc <cmd> --dry-run records what the command would do and prints a numbered would-do log -- a dry deploy names the Cloudflare Pages deployment or the gh-pages force-push (with the reason it is granted) without performing either. Two consequences to plan for: mutating commands now ask for confirmation, so non-interactive callers (CI, release hooks, scripts) must pass --yes or they exit with stdin is not interactive; and selfdoc check's own --dry-run flag is gone, replaced by the framework flag of the same name -- the reserved names --dry-run, --yes, --quiet and --verbose now belong to the framework and are accepted anywhere in argv.
  • [selfdoc] **selfdoc deploy now asks before it runs.** It is the one selfdoc command whose effects leave the machine and land on a live, publicly-visible site: a Cloudflare Pages deployment is live the moment it lands, and the GitHub Pages provider force-pushes gh-pages, so the previously published tree is gone from the remote. It is declared consequential, so the framework prompts before dispatch and refuses on non-interactive stdin. Pass --approve-consequential from scripts, CI and release pipelines. Every other selfdoc command -- gen, build, check, init, serve, gen-data, baseline accept -- is unaffected and still runs bare.
  • [selfdoc] **--yes is gone; the confirmation-skip flag is --approve-consequential.** strictcli 0.36.0 replaced the inferred "mutating command means prompt" rule -- which caught two thirds of every CLI in the fleet -- with a per-command consequential declaration, and the framework now prompts only for commands that make it. yes is a banned flag name, so selfdoc <cmd> --yes is a hard unknown flag error rather than a no-op. In practice this means the mutating commands that used to demand --yes from every non-interactive caller now need nothing at all.

#0.34.0

Extractor coverage fixes (re-exports, module constants, docstring-less/pydantic classes), new selfdoc quality command, configurable coverage_threshold, three new guides, plus breaking removal of post/assembly subcommands and new explicit deploy-target and assembly config keys

Context

Coordinated minor batch release; the previous batch shipped 2026-07-11. This ships roughly three weeks of accumulated backlog for the selfdoc CLI.

Breaking changes in this batch: the selfdoc post and selfdoc assembly subcommand stubs are gone (that surface lives in selfblog now); deploy_github_pages requires an explicit target instead of inferring it from the current directory's origin; and the assembly config keys changed (topology.assembly is rejected in favour of assembly.repo, and assembly.pages_project is now required).

Features: a new selfdoc quality command scoring documentation on a 0-5 tier scale, a configurable coverage_threshold in selfdoc.json, the gen --version-override release handshake, and new directives/quality/blog guides. Fixes tighten ref coverage accounting (re-exports and module constants are now counted, docstring-less and pydantic classes are no longer dropped), make manifest generation idempotent so --no-allow-dirty releases stop looping, honour package-level excludes, and add remediation hints to DRIFT001/STALE001.

#Breaking

  • [selfdoc] **deploy_github_pages requires an explicit target.** The re-exported selfdoc.deploy.deploy_github_pages now takes a required target keyword instead of force-pushing to whatever origin the current working directory resolves to. selfdoc deploy itself is unchanged.
  • [selfdoc] **selfdoc post and selfdoc assembly are gone.** The ten stub subcommands that only printed "moved to selfblog" are deleted; use the selfblog CLI, which has owned these commands since selfblog was split out. Removing them also un-pollutes selfdoc's strictcli schema, so README's command table is generated from the live schema again instead of being hand-maintained.
  • [selfdoc] Assembly config keys changed. topology.assembly is rejected outright (use assembly.repo), assembly.pages_project is now required for projects that publish into an assembly, and topology.legacy_blog_host is new. topology.posts_base now means the canonical blog URL under the docs site, not a separate blog host.

#Features

  • [selfdoc] Version-override handshake. selfdoc gen --version-override <v> stamps an explicit project version into version-bearing generated root files, and selfdoc check --version-override <v> states the version they are expected to embed. Release orchestrators pass the about-to-be-released version so generated files are no longer committed one release behind. The new VER004 check turns a stale embedded version into a hard failure instead of a silent lag.
  • [selfdoc] **coverage_threshold in selfdoc.json.** New optional float field (0.0-1.0, default 1.0) setting the minimum fraction of public symbols that must be documented for selfdoc check to pass. Lower it below 1.0 to accept partial coverage.
  • [selfdoc] **New selfdoc quality command.** Scores a single project on a 0-5 documentation tier scale with a letter content grade (documentation-to-code ratio, excluding test LOC and templates) and prints the next tier's requirement as concrete guidance.
  • [selfdoc] Three new guides. Added a directives guide, a quality/tier guide, and a blog-post authoring guide to the documentation.

#Fixes

  • [selfdoc] Docs. README now documents post/assembly as selfblog commands.
  • [selfdoc] Coverage respects package-level excludes. A gen.exclude package path (e.g. a vendored package) now excludes its files from coverage counting, matching gen's behavior; previously excluded Go packages leaked into coverage as unreferenced symbols and could fail the coverage threshold.
  • [selfdoc] No more STALE001 on every release. The generated CLI index page now renders its version through a var directive instead of a baked literal, so a version bump no longer moves the page's content hash. Projects that hit STALE001 on the CLI index at every release -- and had to run selfdoc baseline accept each time -- no longer do.
  • [selfdoc] DRIFT001 tells you how to resolve it. Drift errors reported by selfdoc check now end with a remediation hint naming selfdoc baseline accept <page>, so an operator whose description is genuinely still accurate has a stated way out instead of having to invent a description edit.
  • [selfdoc] Release-time version handshake is documented. The rlsbl integration guide now covers gen --version-override / check --version-override and the VER004 check, and the staleness guide no longer claims that a page embedding project.version via a var directive changes its content hash on every release (it does not -- the content hash has used the raw template body since hash schema v2).
  • [selfdoc] **selfdoc.quality is documented.** Every public function in the quality-scoring module -- tier computation, content grading, the LOC counters, and the report formatters -- now carries a docstring, so its API reference page has real content instead of bare signatures.
  • [selfdoc] The dependency claims in the docs are true now. The README, homepage, getting-started guide, and comparison page each claimed selfdoc had one runtime dependency (or zero); it has two direct ones -- strictcli and selfdoc-core -- which bring strictspec and tomlkit. All four are pure Python, and the pages now say exactly that.
  • [selfdoc] The check guide's lint-code table is complete. It listed only the SEO rules and STALE001; the codes selfdoc check can actually emit -- DRIFT001, STALE002, CLI001, CLI002 and VER001-VER004 -- now have rows too, each with its severity and how to clear it.
  • [selfdoc] **ref output now counts __init__.py re-exports and module-level constants.** The Python extractor emitted ### headings only for classes and functions, so __all__ re-exports (from ._impl import X) and module-level constants (__version__) were structurally uncountable in coverage even though they sit in the denominator. They now get headings, and ref target=X resolves a name that is only a re-export instead of erroring not found.
  • [selfdoc] **Docstring-less public classes are no longer dropped from ref output.** A public class with no docstring, no methods, and no @dataclass decorator was silently omitted. Any class named in __all__ now always renders at least a heading and signature, and pydantic BaseModel subclasses get the same field-table rendering as dataclasses.
  • [selfdoc] **selfdoc gen no longer rewrites .selfdoc/manifest.json when nothing changed.** The manifest was rewritten on every run because its last_gen timestamp always differs, dirtying the working tree and breaking --no-allow-dirty release flows in a retry loop. It is now written only when its content (ignoring the timestamp) actually changes.

#0.33.0

table-commands discovers the strictcli schema automatically (path attribute removed; schema-dir disambiguates multi-schema repos)

Context

Phase 9.5 coordinated breaking release. table-commands no longer accepts a path attribute; it auto-discovers the unique .strictcli/schema.json by walking the project root, with an optional schema-dir attribute to disambiguate repos that carry more than one schema. Directive attribute handling is now strict across gen and check: an unknown or missing attribute is a hard error (exit 1) reporting the file, line, directive, and allowed attributes.

Because table-commands path removal is a breaking directive change, all in-tree docs and the eight external consumer repositories that use table-commands were migrated to the new bare-directive syntax in lockstep before this release, so no consumer is left on a directive that would now hard-error.

#Breaking

  • [selfdoc] Strict directive attributes. selfdoc check and selfdoc gen now hard-error (exit 1) when a directive uses an unknown attribute or omits a required one, reporting the file, line, directive, and allowed attributes.
  • [selfdoc] table-commands auto-discovers the CLI schema. table-commands no longer takes path; it finds .strictcli/schema.json by walking the project root. Migration: drop path, and if discovery is ambiguous add schema-dir="<dir>".

#0.32.0

Description ownership by content: handwritten text is never overwritten; machine placeholders identified by hash/template/live-recompute

Context

gen now classifies a page's description text itself -- via current/historical module templates, the per-page seed hash, or a live recompute of CLI defaults -- instead of trusting the seeded flag. Hand-rewritten descriptions survive regeneration even with a stale seeded:true marker, and the STALE001/DRIFT001 exemption keys on the ownership predicate so hand-described generated pages get full staleness protection.

#Fixes

  • [selfdoc] Hand-written descriptions on generated pages are never overwritten. selfdoc gen now decides whether to reseed a description by classifying the text itself, so a description you rewrote by hand survives regeneration even if a stale seeded: true marker was left behind.
  • [selfdoc] Hand-described generated pages get full stale-description protection. The STALE001/DRIFT001 exemption now applies only to pages whose description is genuinely machine-generated, so a generated page you have described by hand is checked for staleness like any other page.
  • [selfdoc] **A legacy auto-generated API reference index description is refreshed on selfdoc gen.** Projects still carrying the old "Complete auto-generated API reference index …" placeholder now have it reseeded with a project-specific description instead of being frozen as if hand-written.

#0.31.0

New name config key, gen-index naming fix, legacy description reseed, CLI defaults are now complete sentences, and the list-features directive is removed (migrated to list-modules).

Context

Descriptions are handwritten-first with machine placeholders: auto-seeded page and CLI descriptions are recognizable so they can be reseeded, while hand-authored frontmatter is left verbatim. The old 155-character truncation produced mid-sentence cuts, so machine placeholders are now complete first sentences. list-features is superseded by list-modules, which yields richer module summaries.

#Features

  • [selfdoc] Auto-seeded page descriptions are complete sentences. Generated module page descriptions now carry the whole first sentence of the docstring, no longer capped at 155 characters or cut with an ellipsis.

#Fixes

  • [selfdoc] SEO009 no longer misfires on wrapped first lines. The advisory description-length check now measures the complete first sentence of the opening paragraph (wrapped lines joined) instead of just the first physical line, so pages with a long but soft-wrapped opening sentence no longer get a spurious 'description too short' warning.
  • [selfdoc] CLI page descriptions are the complete first sentence of the help text. Auto-generated strictcli command and group reference pages now seed the full first sentence instead of a 155-character mid-sentence cut, and pages still carrying the old truncated default are correctly re-seeded rather than frozen as if hand-edited.

#0.30.0

gen-index descriptions: correct project naming, legacy wrong descriptions self-heal; no more phantom STALE/DRIFT on generated pages.

#Fixes

  • [selfdoc] Correct API reference index description. selfdoc gen no longer bakes an arbitrary source folder's name into the API reference index for multi-source projects (it uses the configured name or a generic phrasing), and it now refreshes index pages that still carry the old hardcoded description instead of preserving it forever.
  • [selfdoc] No more phantom staleness errors on auto-generated pages. selfdoc check no longer reports STALE001/DRIFT001 on skeleton (generated + seeded) pages whose source changed -- those pages advance automatically instead of being stuck in an unfixable error.

#0.29.0

baseline accept command and gen-index staleness fix

Context

Adds selfdoc baseline accept for clearing staleness dead-ends on pages with dynamic content. Fixes gen-index to use content-aware seeded descriptions, preventing STALE001 deadlocks on generated index pages.

#Features

  • [selfdoc] **selfdoc baseline accept <page>...**. New command to clear a STALE001/DRIFT001 dead-end when a page's content changed but its existing frontmatter description was reviewed and is still accurate (e.g. pages embedding project.version that change every release). It advances the named pages' hash baselines exactly as if the description had been rewritten. Pages are named explicitly (no --all/glob/--force); accepting a page that does not exist, has no baseline, or is not currently stale/drifted is a hard error.

#Fixes

  • [selfdoc] Fix. gen-index description is now content-aware with a seeded marker, preventing STALE001 deadlocks on generated index pages.

#0.28.1

Fix CI: install playwright browsers before running tests, fix publish gate regex

Context

v0.28.0 CI failed because playwright browsers were not installed. The publish gate regex also did not match the actual CI job name. Both are fixed.

#Fixes

  • [selfdoc] Fix CI. Install playwright browsers before running tests so CI passes and the package publishes to PyPI.

#0.28.0

Monorepo conversion with three independent packages (selfdoc, selfdoc-core, selfblog).

#Breaking

  • Blog functionality moved to selfblog. Post discovery, post checks (POST001-POST005), and the unified multi-project check now live in the selfblog package. selfdoc post/selfdoc assembly commands temporarily delegate to selfblog (install with pip install selfblog); selfdoc check on unified projects now hard-errors and directs to selfblog check. New selfblog check command validates posts and unified sites.
  • [selfdoc] Packaging split. pip install selfdoc no longer bundles the selfdoc_core and selfblog packages: selfdoc-core is now a regular dependency, and selfblog is a separate optional install. The selfblog console script moved to the selfblog package -- install it with pip install selfblog. The perf/og extras now route to selfdoc-core[perf]/selfdoc-core[og].
  • [selfdoc] selfdoc post/assembly commands removed -- use the selfblog CLI (pip install selfblog). Unified and posts-only builds through selfdoc build/serve now hard-error directing to selfblog

#0.27.0

Three-package architecture (selfdoc_core + selfdoc + selfblog), forced-rewrite staleness, per-language list-modules, post revisions.

Context

Splits the codebase into three packages: selfdoc_core (shared build engine and extractors), selfdoc (CLI and documentation generation), and selfblog (blog assembly and CLI). This sets up the future monorepo conversion. Staleness detection gains baseline-hold semantics (baseline does not advance while errors remain), schema-hash gating with seeded markers for CLI pages, and root-file template validation. The list-modules directive now groups modules by language with extractor summaries. Post infrastructure adds a revisions sidecar (content-hash gated revision tracking) and localStorage read indicators. Manifest loading uses a tolerant-reader contract with centralized compatibility checks for forward-compatible schema evolution.

#Features

  • Root-file template validation. selfdoc check now validates directives in root-file templates (docs/_CLAUDE.md, docs/_README.md).
  • Staleness baseline freeze. Baseline does not advance while errors are outstanding, preventing silent degradation.
  • CLI page schema-hash gating. Staleness detection uses seeded markers for CLI page schema hashes, preventing false positives on unchanged content.
  • Module listing improvements. Per-language grouping with extractor summaries and hard error for unsupported languages.
  • Three-package architecture. Split into selfdoc_core (shared engine), selfdoc (CLI + docs), and selfblog (blog + assembly). Modules moved to their owning packages with re-export shims for backward compatibility.
  • selfblog CLI entry point. New python -m selfblog CLI with post (new, list, generate, publish), assembly (init, push, status, rebuild, generate-shared), and build commands. Per-package pyproject.toml files for future monorepo conversion.
  • Tolerant-reader manifest contract. Centralized compatibility check for manifest schema versions, allowing older readers to process newer manifests safely.
  • Post revisions sidecar. Content-hash gated revisions.json tracks edit history per post with automatic revision capture on content changes.
  • Read indicators. Posts track read status in localStorage, enabling visual distinction between read and unread content.

#0.26.1

Assembly workflow cleanup and posts.repo configuration.

#Fixes

  • Assembly workflow cleanup. Removed the 42-line inline Python fallback for selfdoc <= 0.24.0, simplifying the assembly CI workflow.

#0.26.0

Posts repo archival, blog redirect via Pages Function, assembly workflow fixes.

#Features

  • Posts repo archival. New posts.repo config field archives resolved post content to a git repo during post publish.
  • Blog redirect via Pages Function. Replace Cloudflare Pages _redirects with _worker.js for host-specific blog.smmh.dev redirect.

#Fixes

  • Assembly workflow fix. Pass --docs-base to generate-shared in the assembly workflow.

#0.25.1

Fix VER001 for assembly builds, Playwright CI setup, test fixes.

#Fixes

  • VER001 version filter. selfdoc build --version X no longer validates old versions that can't be extracted from shallow clones, fixing assembly builds for multi-version projects like claudestream.
  • Playwright CI. CI workflow now installs Playwright browser binaries, fixing 75 test errors that blocked the v0.25.0 publish.

#0.25.0

Local post publish, portfolio homepage, monorepo labels, and blog URL fixes.

#Features

  • Local post publish. selfdoc post publish now builds posts locally and pushes directly to the assembly repo via Git Data API, eliminating the requirement to push to the project's remote. Posts can be published without software releases.
  • Portfolio homepage. selfdoc assembly generate-shared gains --portfolio-file flag to use a custom HTML file as the site homepage. Project listing moves to /projects/.
  • Monorepo label. Projects with version 0.0.0 show 'monorepo' instead of a version badge on the homepage.

#Fixes

  • Dependency installation no longer requires machine-local checkouts: uv.lock previously carried local paths for strictcli and predraw, breaking installs and CI outside the author's machine. Dependencies now resolve from PyPI (strictcli 0.24.0, predraw 0.2.1).
  • Python version requirement corrected. requires-python bumped from >=3.11 to >=3.12 to match actual compatibility (CI tests 3.12/3.13/3.14 only; codebase uses PEP 701 syntax). Dead tomli fallback code removed; ruff config added with target-version = "py312".
  • Expanded CLI help text. All command and flag help strings now meet the 50-character minimum for documentation quality checks.
  • Expanded CLI command help text. All command help descriptions now meet the 40-60 word target for AI citation quality, and include numeric data points where applicable.
  • Blog index URLs. Fixed broken blog post URLs where project slugs were treated as hostnames.

#0.24.0

Migrated all boolean CLI flags to explicit-default style via strictcli.

Context

All bool flags now use --flag/--no-flag syntax with explicit defaults, following the strictcli migration. --no-commit renamed to --auto-commit, --dry-run and --drafts require explicit values, --from-release is now required.

#Breaking

  • Breaking: --no-commit renamed to --auto-commit (default=True). --dry-run and --drafts now require explicit defaults. --from-release is now required.

#0.23.0

Posts-only build mode and post publish command for publishing blog posts without software releases.

#Features

  • Posts-only build. New --target posts flag on selfdoc build for building only blog posts without the full documentation site. Used by the assembly CI for publishing posts without a software release.
  • Post publish command. New selfdoc post publish dispatches blog posts to the documentation assembly without requiring a version bump. Validates posts are committed and pushed before dispatching.

#Fixes

  • Assembly workflow robustness. Multi-version projects with failed version detection now error clearly instead of silently attempting a full build. Post-manifest overlays enable updating posts independently from docs.

#0.22.1

Fix null manifest fields crashing assembly shared element generation.

#Fixes

  • Fix. Assembly shared element generation no longer crashes when manifest fields are null.

#0.22.0

Assembly Stage 2: multi-project site aggregation with shared elements, search indexing, and persistent combined site.

Context

Rewrites the assembly workflow to implement Solution 10: the assembly repo stores the complete combined site. Each dispatch replaces one project's subtree, regenerates shared elements (homepage, blog index, navigation, feeds, sitemap), runs Pagefind for cross-project search, and deploys the full site to Cloudflare Pages.

#Features

  • Assembly Stage 2. New selfdoc assembly generate-shared command and rewritten assembly workflow for multi-project site aggregation. Each dispatch now surgically updates one project, regenerates shared elements (homepage, blog index, nav, feeds, sitemap, search), commits the combined site, and deploys.

#Fixes

  • CF Pages deploy fix. Deploy to the correct Cloudflare Pages project name (smmh instead of docs-smmh-dev).

#0.21.1

Fix auto-commit with gitignored files, fix inline directive regex, add directive name validation

#Features

  • New. Inline directives now validate names against known directives, matching existing behavior for standalone and block directives. Custom directive names in selfdoc.json are validated at load time.

#Fixes

  • Fix. Auto-commit no longer silently fails when gitignored files are in the commit list. Gitignored files are filtered out and commit errors are now visible on stderr.
  • Fix. Inline directives no longer capture trailing punctuation. (v:-: name). now resolves correctly instead of greedily capturing name). as the directive name.

#0.21.0

Posts system, page types, unversioned pages, deployment topology, assembly infrastructure, Pagefind search, and shared elements for multi-project documentation sites.

Context

This release introduces a complete blog/posts system integrated with rlsbl releases, a page type system with configurable schema.org mappings and per-type layouts, unversioned pages for content that persists across doc versions, a deployment topology config for multi-project unified sites, an assembly system that manages a throwaway GitHub repo for aggregated documentation deployment to Cloudflare Pages, Pagefind as a new search engine option with chunked indexes and filtering, and shared elements generation (homepage, blog index, nav, feeds, sitemap) for assembled sites.

#Features

  • Page type system. Frontmatter-driven page types with configurable schema.org mapping, per-type layout control, and type-aware labeling.
  • Unversioned pages. Pages with versioned: false frontmatter build at stable URLs outside the version path, with persistent navigation and search inclusion.
  • Blog posts. New posts system with selfdoc post new, selfdoc post list, selfdoc post generate --from-release, date-based navigation, auto-generated listing page, type-aware feed ordering, draft support, and lint rules POST001-POST005.
  • Deployment topology. Configurable multi-project URL generation with TopologyURLBuilder, cross-project linking, and topology-aware var directives.
  • Assembly system. selfdoc assembly init/push/status/rebuild commands for managing a unified multi-project documentation site deployed to Cloudflare Pages.
  • Shared elements generator. Homepage, blog index, global navigation, unified Atom feed, sitemap, and cross-project link validation for assembled sites.
  • Pagefind search engine. New pagefind option for search_engine config with chunked lazy-loaded indexes, Cmd/Ctrl+K shortcut, and filtering by project, type, version.
  • Topology migration script. scripts/migrate_topology.py for applying topology config across multiple projects.

#Fixes

  • Bug fixes. Wire schema_types to generate_html, propagate topology URLs to auxiliary files, fix nested index.md URL segments, inject posts for constituent projects in unified builds, fix cross-project link validation for URL-form paths.

#0.20.0

ref+target single-symbol rendering, Go dotted method names, expanded CLI help texts.

Context

ref directives now respect the target attribute across all 9 extractors, rendering a single symbol instead of the entire module. This makes PARAM001/RETURN001 checks reachable for targeted documentation. Go public_symbols returns Type.Method for methods, fixing collision bugs. CLI help texts expanded across selfdoc, rlsbl, safegit, and wesktop to satisfy CLI002.

#Features

  • New feature. ref directives now support the target attribute to render a single symbol instead of the entire module.

#Fixes

  • Bug fix. Go public_symbols now returns dotted Type.Method names for methods, fixing collision when two types share a method name.

#0.19.1

Remove version_source config field (redundant with rlsbl).

Context

version_source was added in v0.16.0 to read the project version from manifest files. Investigation revealed rlsbl already bumps selfdoc.json during releases via _bump_selfdoc_version, making version_source redundant. Reverted to reduce complexity.

#Breaking

  • Breaking. Removed version_source config field. rlsbl already handles selfdoc.json version bumping during releases.

#0.19.0

Dotted target syntax, TS re-export fix, CLI002 lint, gen.py protocol integration, coverage matching fix, and TS module JSDoc fix.

Context

symbol_details across all extractors now supports Type.Method dotted targets for class member disambiguation. TypeScript _extract_exports handles export { X } re-exports. Coverage matching uses heading-based regex instead of fragile substring check. gen.py uses module_docstring protocol for all 9 languages (7 gained auto-descriptions). CLI002 validates help text length. TS _extract_module_jsdoc no longer misclassifies function JSDoc as module-level.

#Features

  • New lint. CLI002 warns when CLI help texts are shorter than 50 characters (commands, flags, args, and group subcommands).
  • All languages get auto-extracted descriptions. gen now uses the module_docstring protocol method for all extractors instead of hardcoded Go/Python paths. TypeScript, Zig, Swift, Kotlin, Svelte, Dart, and SQL pages now show real module descriptions.

#Fixes

  • Fixed. TypeScript module-level JSDoc detection no longer misidentifies function-attached JSDoc as module documentation.
  • Fixed. Coverage computation no longer falsely matches symbols that are substrings of other symbol names.
  • Fix. TypeScript ref output now includes re-exported symbols (export { X } and export { X } from '...'), fixing coverage reports that showed them as undocumented.

#0.18.0

module_docstring and symbol_details protocol methods for all extractors, handler signature refactor, and inline directive fixes.

Context

Handler functions now receive path and target as separate arguments, eliminating the latent arg-combining bug. module_docstring protocol method replaces language-string dispatch in staleness.py, enabling DRIFT001 across all languages. symbol_details implemented for all 9 extractors, enabling PARAM001/RETURN001 cross-language. Double-backtick inline directive spans now masked correctly. Inline pass 2 fixed to split multi-line resolver output before fence tracking.

#Breaking

  • Breaking. Handler functions now receive path and target as separate arguments instead of a combined string.

#Features

  • TypeScript extractor. Implement symbol_details method for extracting function parameters, types, return types, and JSDoc documentation status.
  • Swift extractor. Implement symbol_details for function parameter, return type, and documentation extraction.
  • Go extractor. Implement symbol_details for extracting function parameter types, return types, and documentation status from Go source files.
  • Zig extractor. symbol_details method for function parameter and return type extraction.

#Fixes

  • Fix. table-commands directive now correctly joins path attribute with base_dir for monorepo sub-project schema lookup.
  • Fix. Inline directives inside double-backtick code spans are no longer resolved.
  • Fixed inline directive resolution inside code fences. Directives shown as examples inside code fences in resolved content (e.g., the catalog directive's output) were incorrectly matched and resolved by pass 2, causing errors like 'list-modules returned multi-line output'. Fence tracking now correctly handles multi-line resolver output.

#0.17.0

Inline directives, 10 new doc quality checks, XREF002 Go fix, and ecosystem deployment.

Context

Five new lint codes (EXAMPLE001, CLI001, DRIFT001, PARAM001, RETURN001) complete the doc quality pipeline. Inline directive support enables mid-line dynamic values via a two-pass resolver. Protocol extended with symbol_details() and structural docstring parser. XREF002 fixed for Go package directories. Ecosystem: version_source migrated to 17 projects, extractors deployed to incantino/F/gamehome.

#Features

  • New feature. CLI001 lint checks CLI reference completeness for strictcli-based projects.
  • New feature. EXAMPLE001 lint validates Python and JSON syntax in code blocks.
  • New feature. DRIFT001 lint detects when source docstrings change without corresponding documentation updates.
  • New feature. PARAM001 lint warns when function parameters are not documented.
  • New feature. RETURN001 lint warns when return types are not documented.
  • New feature. Inline directive support: self-closing directives can now appear mid-line for dynamic values in prose.

#Fixes

  • Fix. XREF002 no longer fires false positives on Go package paths (directories).

#0.16.0

Five new language extractors (Swift, Kotlin, Svelte, Dart, SQL), version_source config, and doc quality checks.

Context

This release adds five new language extractors: Swift (doc comments, struct schemas, public symbol detection), Kotlin (KDoc parsing), Svelte (component props, instance/module exports, JSDoc), Dart (class/mixin modifiers, doc comments, part file following, export following with show/hide combinators), and SQL (PostgreSQL DDL -- CREATE TABLE/VIEW/TYPE/FUNCTION and COMMENT ON). It also introduces the version_source config field for reading the project version from the primary manifest, and four new doc quality lint checks: XREF001 (internal page link validation), XREF002 (directive path validation), DQ001 (description restates symbol name), DQ002 (description too short), and DQ003 (function-referencing pages need substantive descriptions). Several base extractor utilities were refactored into shared helpers to reduce duplication across extractors.

#Features

  • New feature. version_source config field for reading version from primary manifest.
  • New feature. Swift language extractor with doc comment parsing, struct schema extraction, and public symbol detection.
  • New feature. Kotlin language extractor with KDoc parsing.
  • New extractor. Svelte language extractor for component documentation -- extracts props, instance/module exports, and JSDoc from .svelte files.
  • New feature. Dart language extractor with class/mixin modifier detection, doc comment parsing, part file following, and export following with show/hide combinators and cycle detection.
  • New feature. SQL schema extractor for PostgreSQL DDL -- parses CREATE TABLE/VIEW/TYPE/FUNCTION statements and COMMENT ON documentation from .sql files.
  • New feature. XREF002 lint check -- verifies that resolved directive source files actually exist on disk.
  • New feature. Doc quality checks: XREF001 (broken page links), DQ001 (restated description), DQ002 (short description), DQ003 (missing function info).

#Fixes

  • Fix. Svelte detection now runs before TypeScript in auto-detection order, preventing Svelte projects from being misidentified as TypeScript.

#0.15.1

Fix pipe escaping in unclosed backtick spans.

#Fixes

  • Bug fix. Pipes inside unclosed backtick spans in table cells are now correctly escaped.

#0.15.0

Generic markdown table renderer as public API, with full internal refactoring.

Context

Added render_markdown_table in selfdoc.tables -- a public utility for custom directive authors to render markdown tables with alignment, pretty-print, and pipe escaping. Refactored all 25 internal table-building callsites to use it, eliminating duplicated table construction across extractors, content directives, and strictcli support. Also updated html.py to handle escaped pipes and alignment markers in table parsing.

#Features

  • New utility. render_markdown_table in selfdoc.tables -- public API for rendering markdown tables with alignment support, pretty-print mode, and backtick-aware pipe escaping.
  • Init command. Detects all languages in multi-language projects for config generation.

#0.14.1

Exclude Python test files from coverage

#Fixes

  • Fix. Python test files (test_*.py, conftest.py) and test directories (tests/, test/, __tests__/) are now excluded from documentation coverage, matching what selfdoc gen already excluded.

#0.14.0

Stub extractors for unsupported languages, skip .venv in source walks, remove javascript alias

#Breaking

  • Breaking. Removed javascript alias from the extractor registry. Use typescript for both TypeScript and JavaScript files.

#Features

  • New feature. Unsupported languages in source entries get a stub extractor instead of a config error. Supported languages work normally alongside unsupported ones. LANG001 lint error reports which source paths lack an extractor.

#Fixes

  • Fix. Source walks now skip .venv, node_modules, __pycache__, and other non-source directories. Previously, a .venv/ inside a source path would generate hundreds of doc pages for third-party packages.

#0.13.1

Fix Go root package ambiguity in multi-language projects

#Fixes

  • Fix. Go root packages in multi-source-path projects no longer crash with ambiguity errors. Each source path's root package gets a unique qualified path instead of the ambiguous path=".".

#0.13.0

Multi-language project support and Zig extractor

#Breaking

  • Breaking. Top-level language config field removed. Each source entry is now an object with path and language fields: {"path": "src/", "language": "python"}.

#Features

  • New feature. Zig language extractor with support for pub fn, pub const, pub var, struct/enum/union declarations, /// doc comments, //! module docs, and test block extraction.
  • New feature. Resolver dispatches directives to the correct language extractor based on source path. Cross-language path ambiguity is detected and errors.
  • New feature. selfdoc gen and selfdoc check work across multiple languages in a single project. Coverage enforcement applies to all languages -- undocumented symbols in any language cause failure.
  • New feature. list-modules directive auto-detects language from source path. project.language variable returns comma-separated list for multi-language projects.

#0.12.0

#Breaking

  • 100% coverage enforced. selfdoc check now requires all public symbols to be documented on non-skeleton pages. The min_coverage config field is removed.

#Features

  • Extractor accuracy. Python extractor respects __all__ for public symbol detection. Go extractor handles exported symbols inside const() and var() blocks.
  • Stale file tracking. selfdoc gen now tracks and commits deleted stale doc files alongside newly generated ones.
  • Schema provenance. selfdoc gen and selfdoc check validate that .strictcli/schema.json belongs to the current project via a project_id field.

#Fixes

  • Hash consistency. selfdoc gen updates content hashes after generating, eliminating the 'run check twice' ritual. Gen, check, and build now use consistent locale-prefixed hash keys.
  • Skeleton-only display. selfdoc check now shows which symbols are only referenced on skeleton pages when documented coverage is below 100%.
  • Wider lint suppression. lint_ignore config now accepts all lint code formats (e.g., STALE001, VER002), not just SEO codes.
  • Safer stale cleanup. Stale generated files are now deleted after new files are written, preventing data loss if generation fails midway.
  • Git command safety. All git subprocess calls use -- separator before filenames, preventing filenames from being mistaken for git options.

#0.11.0

#Features

  • New feature. Config-driven redirects for renamed/deleted doc pages. Add a redirects list in selfdoc.json with {from, to} page slugs — the build expands across all locale/version combos, generating both Cloudflare _redirects rules and HTML meta-refresh pages. Existing pages in cached old versions are preserved.
  • Docs. Added prose documentation for the exclude attribute on table-schema and table-config directives.

#0.10.0

#Features

  • New feature. exclude attribute for table-schema and table-config directives, allowing users to exclude specific top-level keys when rendering JSON/TOML/JSONC config files as tables.

#Fixes

  • Bug fix. Go extractor table-schema now correctly renders JSON/TOML config files instead of failing with 'no struct types found' when the file exists in the project.

#0.9.1

#Fixes

  • Fix. Go gen root package now correctly uses ref path="." instead of the module name.

#0.9.0

#Features

  • New feature. Consolidated version detection with new VER002/VER003 lint codes that error when selfdoc.json version drifts from the project manifest.
  • New feature. table-endpoint content directive renders REST API documentation from OpenAPI 3.x JSON specs.
  • New feature. Two-tier coverage reporting distinguishes truly documented symbols (on human-written pages) from merely referenced symbols (on auto-generated skeleton pages).
  • New feature. selfdoc.json now supports an optional version field for explicit project version declaration.

#Fixes

  • Fix. Go gen now produces per-package (per-directory) documentation pages instead of broken per-file pages.

#0.8.1

#Features

  • Check validates all versions. selfdoc check now extracts and validates old tagged versions, not just the working tree. Monorepo version pinning lets docs-site releases freeze constituent project versions.
  • gen_data dogfooding. selfdoc's own docs now use a gen_data script that extracts the directive catalog from source code.

#Fixes

  • Fix frontmatter list parsing. Use bracket syntax [a, b, c] for list values instead of comma detection, which broke description fields containing commas.

#0.8.0

#Breaking

  • Breaking: always-prefixed URLs. All output now uses /<locale>/<version>/page/ URL structure. versions and locales arrays are now required in selfdoc.json. Version and locale picker dropdowns in the header.

#Features

  • Internal refactoring. Introduced BuildContext/PageContext/SearchEntry dataclasses, extracted JS to real files, decomposed \_wrap\_page into sub-renderers, extracted build\_single from build().
  • Multi-version docs. Build documentation for multiple tagged versions from git history. Version picker, old-version banner, per-version SEO (noindex, canonical to latest), git tag extraction with caching. --version CLI flag for dev builds.
  • Localization support. Parallel locale directories (docs/en/, docs/fa/), multi-locale build loop, hreflang tags, per-locale sitemaps with sitemap-index, backward compat for single-locale projects. --locale CLI flag.
  • Search filters. Filter search by version, locale, group, type, target, project, and tags. Syntax: key=value, key=a|b (OR), -key=value (NOT). Auto-injected version=latest default. Chip UI for active filters.
  • Monorepo unified site. New selfdoc/unified.py module builds one site from multiple projects. Hybrid sidebar with project groups, auto-generated landing page, rlsbl workspace.toml validation, unified search/sitemap/glossary, check_unified for all constituent projects.
  • Documentation overhaul. Rewrote homepage, restructured nav into 4 groups, enriched 20 API reference stubs, populated 23-term glossary, added callouts, created 14 new guide pages (search, SEO, code blocks, custom directives, check, comparisons, rlsbl integration, feeds, llms.txt, glossary guide, multi-language, root files, gen-data, staleness).

#Fixes

  • Fix Python logo SVG. The two snake halves in code block language icons no longer appear separated.

#0.7.0

#Breaking

  • strictcli dependency bumped to >=0.7.0 for --dump-schema support.

#Features

  • Shared resolution pipeline. Directive resolution centralized in a single pass, eliminating redundant walks and double resolution in selfdoc check.
  • **--dry-run flag for selfdoc check.** Reports staleness without writing hashes or committing. Hash computation centralized in staleness.py.
  • Glossary opt-out. Set "glossary": false in selfdoc.json to suppress auto-generated glossary page. Feed entry limit. Set "feed_max_entries": N to limit the Atom feed to the N most recent pages.
  • Schema.json-based CLI extraction. Replaced AST-based CLI structure extraction with reading .strictcli/schema.json. Richer data (flag choices, hidden flags, deprecated commands). Go projects get CLI documentation for the first time.

#0.6.0

#Features

  • Declarative config schema. Config validation is now driven by a schema (FieldType enum + FieldSpec dataclass) instead of procedural code. Unknown top-level keys in selfdoc.json are now rejected.
  • Five new directives. list-modules, table-commands, table-directives, table-config-schema, and var for generating module lists, CLI command tables, directive catalogs, config reference tables, and project metadata interpolation.
  • Root file generation. selfdoc gen can now produce root-level Markdown files (CLAUDE.md, README.md, etc.) from underscore-prefixed templates in docs/. Configure via root_files in selfdoc.json. Generated files are read-only (0o444) with an auto-generated header.

#Fixes

  • Underscore-prefixed templates excluded from lint. selfdoc check now skips _*.md files in docs/, matching the existing build exclusion.

#0.5.2

#Fixes

  • CLI page preservation fix end-to-end. Handwritten CLI page descriptions now actually survive selfdoc gen (the 0.5.1 preservation logic was defeated by stale-file cleanup deleting CLI pages before they could be read for preservation).

#0.5.1

#Features

  • Module page descriptions from docstrings. selfdoc gen now seeds new module pages with the first line of the module's docstring instead of the generic 'API reference for X' template; quote-wrapping length is also fixed.

#Fixes

  • CLI page description preservation. Handwritten description frontmatter on CLI pages (per-command, per-group, and the CLI index) now survives selfdoc gen runs, matching the behavior added for module pages in 0.5.0.
  • Sticky column visuals. Sticky first column in tables now preserves row stripe and hover backgrounds instead of showing a flat fill.

#0.5.0

#Breaking

  • API entry cards default to off. Re-enable via auto_detect.api_entries in config.

#Features

  • Module page descriptions preserved across regenerations. selfdoc gen now reads each existing page's frontmatter; if the description: has been customized (i.e., differs from the default 'API reference for X' template), it's preserved instead of overwritten. Bootstrap workflow: run selfdoc gen once, then handwrite each page's description in its frontmatter — subsequent selfdoc gen runs keep your descriptions.
  • Table redesign. Tables now use content-based widths and support sortable columns.
  • Language icons for code blocks. New display modes: colorful, monochrome, or none.
  • Optional line numbers for code blocks.
  • Subtle code block buttons with a coherent visual design.
  • Configurable run button, page navigation, and page progress indicators.
  • Theme-consistent styling for mark, kbd, abbr, del, ins, small, figure, var, samp, cite.
  • New directives: prose-desc, list-tree, table-dep, list-features.
  • CLI page descriptions use command help text instead of generic boilerplate.

#Fixes

  • Demo knob panel cleanup: removed unused knobs, added font options, fixed bugs.
  • Visual fixes: heading anchor hover, collapsible padding, code background, sidebar spacing.
  • Favicon and OG card now use the theme accent color.
  • Improved coverage matching for generated doc pages.
  • Recursive coverage matching now uses startswith for prefix-based directory matches.
  • code-help directive: path resolution and strictcli flag extraction for Go projects.

#0.4.5

#Breaking

  • Removed deprecated --warn-only flag (warnings are non-fatal by default since 0.4.0)

#Features

  • JSON schema for selfdoc check --format json output (schemas/check-output.schema.json)

#0.4.4

#Fixes

  • Build now cleans the output directory before writing, preventing stale files from previous builds

#0.4.3

#Features

  • Directive catalog now includes descriptions, attribute specs, and usage examples for all core directives
  • Generated API and CLI reference pages now appear in organized sidebar groups

#0.4.2

#Features

  • Auto-commit prefers rlsbl commit when available, marking commits with Autogenerated trailer for changelog coverage exemption

#0.4.1

  • No user-facing changes.

#0.4.0

#Features

  • Directive syntax redesigned: new attribute-based format (:-:, :<:, :>:) with a formal directive catalog replacing the old :::name arg syntax. All existing directive blocks must be migrated. The glossary directive is now list-glossary.
  • Custom directive scripts must update from resolve(arg, config) to resolve(attrs, config, body). Body content is now forwarded.
  • Extractors refactored to a LanguageExtractor protocol with a registry. Custom extractor integrations may need updating.
  • URL scheme changed from flat (guide.html) to directory-index (guide/index.html, served as guide/). All canonical URLs, sitemap entries, and internal links updated. External links pointing to old .html URLs will 404.
  • H1 headings are now auto-generated from the page title. The first # heading in Markdown is consumed as the title source (not rendered as-is). Multiple # headings in a single page now cause a build error.
  • selfdoc build no longer exits 1 for lint warnings. Exit 1 is reserved for errors only. The --warn-only flag is deprecated (warnings are non-fatal by default).
  • selfdoc gen command: auto-generates documentation pages from source code structure, with exclusion patterns, generated: true frontmatter, and stale file cleanup
  • selfdoc gen-data command: runs sandboxed scripts (via bubblewrap) to generate CSV/JSON data files for documentation
  • First-class strictcli support: auto-detects strictcli usage and generates CLI documentation pages
  • Description staleness detection: selfdoc check warns (STALE001) when a page description no longer matches page content, tracked via content hashing in .selfdoc/hashes/
  • Pluggable search engine: choose "builtin", "fuse", or "minisearch" via the search_engine config field
  • Landing page template: hero section with tagline, CTA button, and feature cards, configured via branding config field
  • Cross-page term linking: <dfn> definitions automatically linked across pages with dotted-underline .term-link styling
  • Documentation coverage for Go (exported symbols) and TypeScript/JavaScript (export declarations), previously Python-only
  • Per-symbol coverage tracking with configurable min_coverage threshold
  • Callout directives (note, tip, warning, danger, important) as first-class directive types
  • Feed filtering via feed: false frontmatter; changelog pages auto-detected and excluded by default
  • Reading progress bar fixed below the topbar
  • Scroll affordance gradients on overflowing code blocks and tables
  • Sticky first column on horizontally-scrolling tables
  • auto_detect config field to disable step guide and API entry heuristics globally or per-page via auto_steps/auto_api frontmatter
  • Markdown block tokenizer (selfdoc/tokenizer.py): standalone module with zero dependencies, 10 token types. Used internally for both HTML rendering and lint analysis.
  • Auto-commit system: selfdoc build, check, init, gen, and gen-data now auto-commit changed project files (hashes, generated docs, generated data). Disable with --no-commit. Uses safegit when available, falls back to git.
  • Directive token support in tokenizer: :::name arg / ::: blocks are recognized as structured tokens
  • SearchAction in WebSite JSON-LD on the homepage, enabling Google sitelinks search
  • GitHub Pages security headers: <meta http-equiv> tags for X-Content-Type-Options, X-Frame-Options, and Content-Security-Policy injected when deploy target is github-pages
  • WCAG contrast validation for user custom.css: SEO012 checks CSS variable overrides in docs/custom.css against theme backgrounds
  • High-contrast mode overrides: both themes override --link, --text-secondary, --sidebar-text, and --sidebar-active in prefers-contrast: more media query
  • Heading anchor IDs now deduplicate (appends -1, -2 for repeated headings) and preserve Unicode characters
  • Scrollspy correctly tracks headings when scrolling in both directions
  • Step guide detection tightened: keyword must appear at start of heading text, 200-char lookback (no more false positives on "Next Steps" or "Troubleshooting Steps")
  • API entry wrapping tightened: requires identifier-like heading and single-line code block (no more false positives on tutorial sections)
  • Prev/next links show directional labels ("Previous" / "Next") above page titles
  • Admonition icons use CSS mask-image technique, adapting correctly to dark mode
  • Diff highlighting uses +/- prefix symbols in addition to color
  • Collapsible section indicators replaced with 16x16px SVG chevrons (previously 8x12px CSS triangles)
  • Clean theme link color (#635bff) barely passed WCAG AA in light mode (4.70:1) and failed in dark mode (4.23:1). New color #5046e4 passes comfortably (6.29:1 light, 5.93:1 dark).
  • SEO lint false positives from fenced code blocks: heading counts (SEO001), heading level gaps (SEO002), empty alt text (SEO003), paragraph length (SEO007), empty sections (SEO011), and all other checks now use the tokenizer, making them immune to code block content
  • SEO007 false positives on directive-heavy pages: when a heading is followed by a :::directive block, the short-paragraph warning is suppressed
  • Statistics density check (SEO008) now evaluates prose content only, excluding code blocks
  • md_to_html() refactored from a 180-line line-walking loop into a clean tokenize-then-render pipeline
  • _redirects file is no longer generated (directory-index URLs don't need trailing-slash redirects)
  • _headers file is now only generated for cloudflare-pages deploy target
  • Auto-generated glossary page: collects all <dfn> terms site-wide into an alphabetical glossary with source links
  • Changelog auto-detection: CHANGELOG.md in project root is automatically included as a documentation page
  • selfdoc build --warn-only flag to treat lint warnings as non-fatal
  • Page progress indicator ("Page X of Y") between prev/next links
  • Current page title shown in the topbar on non-index pages
  • URL-triggered search: navigate to ?q=term to open search pre-filled with results
  • Meta description auto-generation: pages without frontmatter description now get <meta name="description"> auto-extracted from the first paragraph
  • Horizontal rule support: ---, ***, and ___ in Markdown now render as <hr>
  • Code tab sync no longer infinite-loops with 3+ tab groups sharing a language
  • Heading copy-to-clipboard shows a toast notification
  • Edit link opens in a new tab
  • OG description falls back to first paragraph when no frontmatter description
  • Each admonition type has a distinct background color
  • Focus indicators use :focus-visible throughout (keyboard-only, no mouse outlines)
  • Sidebar active link has a visible background highlight
  • Mobile sidebar traps focus within the overlay
  • Mobile sidebar closes on Escape key
  • Table rows highlight on hover
  • Cmd+K label adapts to platform (shows Ctrl+K on Windows/Linux)
  • "Last updated" date shown at the top of the page alongside breadcrumbs
  • Search "no results" message includes guidance ("Try different terms or browse the sidebar")
  • Feedback "No" response prompts for written feedback instead of just "Thanks"
  • Negative feedback provides a text input for follow-up
  • <summary> elements have :focus-visible outlines
  • <pre> elements have aria-label describing the code language
  • llms-full.txt includes page boundaries with title headings and path comments

#0.3.1

#Features

  • npm package renamed from selfdoc to selfdocumenting (npm blocks selfdoc due to similarity with abandoned self-doc package). Install via npm install -g selfdocumenting or npx selfdocumenting. The CLI command remains selfdoc.

#0.3.0

#Features

  • base_url is now a required field in selfdoc.json (previously optional)
  • Frontmatter description is now required on every page (auto-extraction removed); missing description is a build error
  • selfdoc build now fails on SEO lint warnings
  • Subdirectory-based nested nav groups with collapsible sidebar sections, localStorage persistence, and frontmatter overrides (nav_group, nav_order)
  • Configurable search trigger via search config field: "icon" (magnifying glass button), "bar" (text input with Cmd+K hint), or "hidden"
  • Functional feedback widget via feedback config field with webhook POST and Google Analytics event support
  • Atom feed generation (feed.xml) with auto-discovery <link> tag in <head>
  • Definition list syntax (term\n: definition) with glossary styling and DefinedTerm JSON-LD
  • Inline stat markup (==value==) producing semantic <data> elements
  • Code tabs for switching between language variants with localStorage persistence
  • Git branch auto-detection for edit links; configurable via branch config field
  • Rich OG card PNG generation with text overlay via predraw (optional dependency)
  • SEO lint framework with 15 rules covering headings, descriptions, images, contrast, and structured data
  • JSON-LD structured data: TechArticle, BreadcrumbList, WebSite, SoftwareSourceCode, Organization/Person, ItemList, DefinedTermSet
  • Open Graph and Twitter Card meta tags with og:locale, og:image:alt, and auto-generated social card images
  • robots.txt with explicit AI crawler permissions (GPTBot, ClaudeBot, PerplexityBot, etc.)
  • Visible "Last updated" dates with <time> elements, dateModified in JSON-LD, and sitemap lastmod
  • selfdoc check --ignore SEO007,SEO008 to suppress specific lint rules
  • selfdoc check --format json for machine-readable output
  • Color-coded selfdoc check output (green/yellow/red by severity)
  • New config fields: lang (BCP 47), author, twitter, branch, search, feedback
  • Sticky table headers no longer hide behind the fixed topbar
  • Copy button now always visible on code blocks (was hidden until hover, invisible on touch)
  • Fixed dark mode contrast for all accent colors
  • Fixed breadcrumb intermediate links pointing to non-existent directory index pages
  • Build-time Pygments syntax highlighting (replaced client-side highlight.js)
  • Build-time CSS, JS, and HTML minification with critical CSS inlining
  • Gzip and Brotli pre-compression of build output
  • Search JS externalized to search.js with lazy index loading
  • Conditional JS inclusion based on page content
  • ARIA labels on sidebar nav, TOC nav, and search dialog
  • Dynamic theme toggle ARIA label indicating current state
  • Roving tabindex on code tabs per WAI-ARIA pattern
  • Heading anchors visible on touch devices
  • Card-style prev/next navigation links
  • Print stylesheet: 2cm margins, forced light colors, hidden breadcrumbs, code wrapping
  • Security headers and trailing slash redirects for Cloudflare Pages
  • Edit link shown at both top and bottom of content area
  • llms.txt and llms-full.txt for AI documentation ingestion
  • selfdoc check reports undocumented public symbols when coverage is below 100%
  • lint_ignore config field for project-level lint rule suppression
  • Edit link and "Last updated" date no longer run together (flex layout with gap)
  • Search shows "No results" message instead of blank space
  • Search dialog closes when clicking a result link
  • Fixed code-block hover shadow invisible in dark mode
  • Table <caption> derived from preceding heading for screen readers
  • 44px minimum touch targets on all interactive elements
  • Admonition icons (distinct SVG per type: info, lightbulb, warning triangle, octagon, exclamation)
  • Styled generic <details>/<summary> in content
  • Styled standalone <dfn> tags outside glossary context
  • RSS feed link in site footer
  • Fragment highlight animation when navigating to #section URLs
  • Topbar truncates long project names with ellipsis

#0.2.0

#Features

  • Theme system with per-project theming via "theme" in selfdoc.json and optional docs/custom.css overrides
  • Syntax highlighting via highlight.js (light + dark themes)
  • Google-style docstring formatting (Args, Returns, Raises rendered as structured lists)
  • Heading hierarchy: directive expansions use h2/h3/h4 instead of injecting h1
  • Module name mangling (selfdoc.extractorsthon bug)
  • Nested _build/_build recursion when rebuilding
  • Deploy supports CF_ACCOUNT_ID and CF_PAGES_API_TOKEN env var names (remapped to wrangler's expected names)
  • Minimal theme: clean typography, dark mode, high-contrast, and reduced-motion variants (all auto-detected from OS preferences)
  • Top bar with project name and version badge
  • Heading anchor links for deep linking
  • CSS extracted to cacheable style.css instead of inlined per page

#0.1.0

#Features

  • Python, Go, TypeScript/JS extractors
  • Custom directive plugins
  • selfdoc check coverage analysis
  • HTML generation with responsive CSS
  • Deploy to Cloudflare Pages + GitHub Pages
  • SSE live reload in selfdoc serve
  • Code-aware static site generator
  • :::directive syntax for embedding code-extracted content
  • 5 built-in directives (module, schema, test, cli, config)
Search