Skip to content

chore: fork delta since upstream f8d91268 (browse-only) - #30

Closed
DaneSpiritGOD wants to merge 36 commits into
baseline/upstream-2026-10-17from
main
Closed

DaneSpiritGOD wants to merge 36 commits into
baseline/upstream-2026-10-17from
main

Conversation

@DaneSpiritGOD

@DaneSpiritGOD DaneSpiritGOD commented Oct 23, 2026 •

Copy link
Copy Markdown
Owner

Fork delta since upstream f8d91268 — browse-only

Do not merge this pull request. It exists so the fork's delta against upstream can be read as a
normal diff (per-file review, inline comments, "Viewed" tracking). baseline/upstream-2026-10-17 is a
frozen upstream snapshot, not a development branch; merging would move the snapshot forward and retire
the comparison. See ADR-0001: fork-local work
lands directly on main and is never offered upstream.

Baseline

base f8d912682 — doocs/md chore(deps): upgrade project dependencies (#1995), 2026-10-17
head 59d74af4f — main
delta 36 commits · 71 files · +6448 −25
upstream not yet merged 0bbfe56b6 deps doocs#1996, 67bba7d68 mermaid doocs#1997 (2 commits, deliberately out of this diff)

Nothing under apps/** or packages/core|shared|api|mcp-server is touched. The fork adds a new
workspace package plus tooling, docs, and workflows; upstream code is only touched at the edges
(package.json scripts, ESLint ignores, .gitignore, CI job tail).

What the delta contains

Theme Commits Footprint
@dane.dai/md-web-cli — headless render CLI 12 packages/md-web-cli/**, 48 new files, +4272
Single verify gate (browser Oracle + byte-exact diff) 16 scripts/verify.mjs, scripts/verify/*, +1486
Publish / sync / CI wiring 4 .github/workflows/{publish-md-web-cli,sync-upstream}.yml, ci.yml tail, +342
Docs, ADRs, agent setup 4 docs/adr/000{1,2,3}, GLOSSARY.md, docs/agents/*, AGENTS.md, +365

1. @dane.dai/md-web-cli (12)

Single-file, zero-runtime-dependency CLI that renders Markdown to WeChat-ready HTML without a browser;
theme and highlight CSS are inlined at build time. Config dialects, flag overrides, --raw, placeholder
failure semantics, and an OIDC-based npm publish path. Tiers and the golden snapshot were retired in
favour of the verify gate below (ADR-0002).

  • e6e7e0b2c feat(md-web-cli): bundle pipeline and minimal render CLI
  • 5c6546a33 fix(md-web-cli): build bundle before tests so the CLI subprocess test finds dist
  • 5270da869 feat(md-web-cli): wechat-ready output and placeholder failure semantics
  • 5db40a80e feat(md-web-cli): config file dialects, flag overrides, --raw and body color
  • 488475f3c fix(md-web-cli): review fixes — boolean flag argv handling, juice fallback warning, env-overridable chromium
  • fb0863097 fix(md-web-cli): order type import before value import
  • a9022310f test(md-web-cli): replace blind test assertions with observable ones
  • cdb293ad2 test(md-web-cli): drop an assertion that could never fail
  • 968654818 test(md-web-cli): assert site-default parity between CLI and app
  • 5c9012002 test(md-web-cli): golden snapshot + tier 2 verify-snapshot gate
  • 7dccaa9f9 chore(md-web-cli): retire the tier machinery, golden snapshot, and oracle scripts
  • a2c3fa69a docs(md-web-cli): consolidate docs and relocate the manual paste check

2. Verify gate (16)

One command builds the web app and the CLI, captures both, and compares byte-exactly against a
browser-driven Oracle, plus structural assertions that survive when pixels legitimately move
(ADR-0003). The gate deliberately landed red and is
non-blocking in CI; making it blocking is an explicit decision, not a default.

  • ca3f7219c feat(oracle): workflow_dispatch browser Oracle rebuild via Playwright
  • 66fe68056 feat(oracle): semantic diff and tier 1 acceptance evidence
  • 3e5ad58fc fix(md-web-cli): semantic-diff tests read the committed oracle evidence copy
  • 822c281a1 fix(oracle): collapse whitespace when comparing extracted CSS values
  • 112b409b7 fix(oracle): parse full color syntax in the semantic extractor
  • e5cceb1b6 feat(verify): make the Oracle a callable capability
  • 2b2e5fcbb chore(spike): run the browser Oracle headless on a GitHub runner
  • 1347468c6 feat(verify): one command that builds, captures, and compares byte-exactly
  • 194abcdb4 test(verify): add an infrastructure-probe fixture
  • 64387a9c5 feat(verify): accept a Designer config and project it to render keys
  • ef960aa46 feat(verify): keep structural assertions and add a reverse-case self-test
  • 01ae26641 fix(verify): keep per-fixture artifact dirs and detect text-only preview changes
  • 59d74af4f fix(verify): close review findings in the gate
  • c8ad1e28c ci: run verify as a non-blocking signal with a cached browser
  • c773bda32 ci: switch publish and sync workflows to verify
  • f157415f8 docs(adr): record the single verify gate and narrow the Oracle glossary entry

3. Publish / sync / CI (4)

4. Docs, ADRs, agent setup (4)

  • 782fa764c feat(agents): add domain documentation and issue tracker guidelines
  • 88ab6c145 docs: add domain glossary and fork-local ADRs
  • f01a5ac36 docs(adr): upstream changes land via a verified sync pull request
  • 1ba701502 chore: ignore local temp/ scratch directory

Upstream-owned files touched

File Change Merge risk
pnpm-lock.yaml +67 (Playwright) High churn — upstream's doocs#1996 rewrites the lock. Resolve by taking upstream's file, then pnpm install.
package.json wcli:verify script; md-web-cli added to type-check:packages / test filters; playwright devDep Low-medium — upstream also edits deps/scripts
eslint.config.mjs one added ignore: packages/md-web-cli/fixtures/ Low — single-line, additive
ci.yml appended steps only: Playwright cache/install, browser-gated CLI test, non-blocking pnpm wcli:verify Low — appends at EOF; conflicts only if upstream appends too
.gitignore appended: .corepack, .playwright-browsers, .wt-ticket-*, .pnpm-store/, temp/ Very low — append-only
AGENTS.md, docs/architecture.md one workspace row + fork-local workflow note Low — table rows upstream rarely touches

How to read this PR

  • Commits tab: all 36 in order, each with its own diff.
  • Files changed: 71 files; tick Viewed per file to track a walkthrough. Highlights are
    packages/md-web-cli/src/{render,wechat,config}.ts and scripts/verify/{compare,oracle}.mjs.
  • Same diff without PR chrome:
    https://github.com/doocs/md/compare/f8d912682...DaneSpiritGOD:md:main
  • Verification: ci.yml runs lint / type-check / test on this PR; pnpm wcli:verify is the gate itself.

Refreshing after the next sync

git fetch upstream
git branch -f baseline/upstream-<new-date> upstream/main
git push -f origin baseline/upstream-<new-date>:baseline/upstream-<new-date>
gh pr create --repo DaneSpiritGOD/md --base baseline/upstream-<new-date> --head main --draft \
  --title "chore: fork delta since upstream <sha> (browse-only)"

Retargeting this PR's base instead of opening a new one keeps the history in one place, but existing
inline comments will be marked outdated. Opening a fresh PR per sync keeps comments attributable.

Checklist

  • Delta computed against the last sync point (f8d912682), not against a moving target
  • Upstream commits not yet merged are listed, not mixed into the diff
  • Never merge — retarget or recreate after each upstream sync

Record the vocabulary and the two decisions that make this fork's
headless CLI work legible: fork-local development (straight to main,
merge upstream in) and shipping the CLI as a single zero-dependency
bundle with themes embedded at build time.

Also ignore the workspace-local corepack cache, used when the home
cache is not writable.
The sync workflow verifies upstream merges in its own workspace and
opens a pull request instead of pushing main directly; the human merge
is a real push that triggers the publish chain, and the review covers
what the snapshot gate cannot.
Add @dane.dai/md-web-cli: an esbuild single-file, zero-dependency Node
bundle of the doocs render core (marked + custom renderer + HTML
post-processing), with the four theme stylesheets and the ten vendored
highlight.js presets imported as text at build time. The render
subcommand emits the intermediate form carrying a themed <style> block;
CSS inlining and placeholder detection land with the next tickets.

Defaults follow the site's default style config (theme default,
#0F4C81, 16px/1.75, mac code block, github-dark), explicitly not the
MCP server's. Conversion makes zero network requests, proven by a test
that hard-blocks net.connect and fetch. A full-coverage fixture
(h1-h6, tables, multi-language code blocks, nested lists, blockquotes,
images, hr) is included for later tickets.

Closes #3
…y color

- Add src/config.ts with two normalizers (Designer export, MCP schema JSON)
  to the internal RenderConfigInput; whitelist-based so junk keys are
  ignored silently. isCiteStatus/isCountStatus map to citeStatus/countStatus.
- Resolve the CSS editor state (cssContentConfig) to the active tab's CSS,
  mirroring the site's apply-only-active-tab behavior; --css-tab overrides
  by id or name. Custom CSS is unscoped from #output and merged after
  theme CSS, before processCSS.
- Add headingStyles and bodyColor (default rgb(10,10,10), applied on the
  output container like the copy flow) to RenderConfigInput and rendering.
- CLI: --config, --css-tab, --raw (pre-inlining intermediate output),
  --body-color and flag overrides; precedence defaults < config < flags.
  Pending-placeholder failure semantics preserved in both modes.
Introduce the Tier 3 Oracle rebuild entry (ticket 06):

- scripts/oracle/rebuild-oracle.mjs: serves the built apps/web/dist on a
  local static server, drives Playwright Chromium headless, injects the
  md-web-cli fixture through CodeMirror's native input pipeline (click,
  select-all, insertText), waits for a stable preview via the app's own
  readiness signals (fixture markers, no pending diagram/KaTeX, HTML
  signature stability), triggers the real WeChat copy flow from the
  header button, reads the processed clipboard HTML back, and writes
  oracle.html + a structural report atomically (temp + rename) only on
  full success.
- .github/workflows/oracle-rebuild.yml: manual workflow_dispatch that
  builds the web app, runs the script, and uploads the Oracle HTML and
  report as the browser-oracle artifact. The browser appears only here,
  never in the recurring CI path.
- playwright added as a root devDependency (the doocs repo has none);
  browser install is a workflow step, locally documented in
  packages/md-web-cli/ORACLE.md.
- ORACLE.md documents how to run it, what the artifact is, and the
  cross-reference to the stored research baseline.
- tests/oracle-summary.test.ts: jsdom smoke test for the report's
  structural-summary extraction rules.
- eslint.config.mjs: un-ignore scripts/ so the new script is linted.
- scripts/oracle/semantic-diff.mjs: property-level comparison of the browser
  Oracle vs the CLI's WeChat-ready output with color/numeric tolerance and an
  explained-differences ledger (class attrs, inline body color, svg attrs,
  link trailing slash, table margin reset)
- scripts/oracle/verify-defaults.mjs: cross-checks site defaultStyleConfig and
  the rendered output (mac code block, github-dark, 16px/1.75/1x, #0F4C81,
  #576b95, legend alt, body color rgb(10,10,10))
- scripts/oracle/screenshot-comparison.mjs: headless Chromium screenshots
- docs/evidence/md-web-cli-tier1/: committed report, screenshots, manual
  WeChat check instructions (PENDING HUMAN CHECK)
- vitest coverage for extraction, tolerance and classification logic
…ce copy

The live oracle artifact is gitignored; the test now falls back to the
committed tier-1 evidence copy so the suite passes on a fresh checkout,
and color assertions are case-insensitive per parseColor normalization.
…et 08)

- commit packages/md-web-cli/golden/snapshot.html, regenerated via
  pnpm oracle:refresh-snapshot (scripts/oracle/refresh-snapshot.mjs)
- add scripts/oracle/verify-snapshot.mjs: standalone, arg-driven gate
  asserting CLI render exit 0, no pending placeholders, WeChat-ready
  inline styles (no class/style tags, block elements styled), and
  semantic property match against the snapshot; --self-test runs the
  three reverse cases (placeholder injection, style stripping,
  truncation) and requires each to fail
- extract shared tolerance/extraction helpers into
  scripts/oracle/lib/extract.mjs, reused by semantic-diff.mjs
- wrap the gate in vitest (packages/md-web-cli/tests/verify-snapshot.test.ts);
  78 tests green
- record the snapshot in the Tier 1 evidence README and in the ticket 06
  oracle-report.json (goldenSnapshotPath)
…esolution (ticket 09)

- add .github/workflows/publish-md-web-cli.yml: push-to-main build + ticket 08
  verify-snapshot gate + unit tests, then publish via npm OIDC trusted
  publishing (id-token: write, no NODE_AUTH_TOKEN, no notification secrets)
- version rule: MAJOR.MINOR hand-edited in manifest; PATCH automatic —
  publish manifest version when (MAJOR,MINOR) differs from registry latest
  or package unpublished, else registry PATCH + 1, applied with
  npm version --no-git-tag-version (no write-back commit)
- scripts/resolve-publish-version.mjs + vitest coverage for the rule
- make package publishable: drop private, add files (dist + README) and
  license, add README
A font-family list wrapping across lines is reformatting, not a visual
change; valuesMatch now normalizes whitespace runs before comparison.
… 10)

Weekly cron + manual dispatch that fetches doocs/md, merges new upstream
commits into sync/upstream, builds md-web-cli and runs the ticket-08
verify-snapshot gate, then opens or force-updates a PR into main whose
body records the verification result and run link. Conflict, build, and
verification failures surface as GitHub Actions notifications with no PR.
PRs from GITHUB_TOKEN carry no checks; the recorded in-workspace
verification is what the human merges on. Merging triggers the ticket-09
publish chain normally.
…lback warning, env-overridable chromium

- Boolean flags (--mac-code-block etc.) now consume the next argv token
  only when it is a boolean literal; a bare flag means true and no longer
  swallows a following positional input.
- mergeCss warns on stderr when every juice attempt fails, so a
  non-WeChat-ready artifact is never silently written.
- Screenshot script's Chromium fallback path is overridable via
  MD_WEB_CLI_CHROMIUM instead of being machine-locked.
- Tier-1 evidence README explicitly records the golden snapshot as the
  Tier-1-confirmed artifact.
parseColor now handles the color forms a CSS serializer can emit, so the
Tier 1 / Tier 2 comparisons no longer treat valid colors as non-colors:

- accept 8-digit (#rrggbbaa) and 4-digit (#rgba) hex with an alpha channel
- convert rgb() percentage channels to 0-255 instead of reading them raw
- round fractional channels and clamp out-of-range channels and alpha
- reject malformed or over-long input instead of returning a half parse

Boundary tests pin the documented limitations that were left unchanged:
valuesMatch ignores units when both sides carry the same number (16px vs
16em), named colors are not parsed, and alpha is compared with tolerance.
Four assertions could not fail, so they proved nothing:

- cli.test.ts pointed HTTP_PROXY/HTTPS_PROXY at a dead port, which Node's
  fetch ignores without --use-env-proxy. offline.test.ts now starts a
  loopback TCP sink that counts inbound connections and a poisoning
  preload, then proves a fixture render leaves both at zero — including
  with every proxy env var pointed at the sink — plus a control that the
  poison really blocks a foreign dial.
- config.test.ts looked for the literal strings `color:red` / `color: red`,
  but the CSS pipeline re-serializes custom CSS, so neither can ever
  appear. Replaced with a color-semantic regex.
- publish-version and semantic-diff gain boundary cases for hex forms,
  rgb percentages, clamping, alpha, numeric tolerance, prerelease and
  build-metadata inputs, and the leftover edge cases are pinned with
  comments so they stay conscious limitations.

Adds output-structure.test.ts: it asserts the CLI-side contract (no
<style>, no class attributes, inline styles everywhere) and documents that
verify-snapshot's --self-test does not cover that axis. Follow-up tracked
in #11.
`expect(html).toMatch(condition ? /./ : /./)` matched any character in
either branch, so the line proved nothing. The neighbouring assertion on
the style block (`#0d1117`) already covers the github-dark background it
was presumably meant to check.
Upstream never lints scripts/; this fork un-ignored it in ca3f721 to
cover the new oracle scripts, which surfaced pre-existing errors in files
this fork does not own. Restore the upstream ignore list entry.

Refs #15
perfectionist/sort-imports expects the type sibling first; eslint --fix was
relocating it above the file-level JSDoc, so place it manually below the
comment instead.

Refs #15
The Oracle rebuild has only ever run locally on macOS. Before anything
depends on a headless browser in CI, prove the path works on a Linux
runner: install Chromium, build the web app, run the unmodified
rebuild-oracle.mjs, and upload the clipboard HTML plus the run log.

Refs #16
The browser capture now serves the built app, drives the copy flow, and
returns the clipboard HTML as a string, writing only into a caller-supplied
temp directory outside the repository. It builds nothing, fails loudly on a
missing build, and overrides the browser via MD_WEB_CLI_CHROMIUM.

The legacy rebuild script stays as a thin wrapper until the committed
Oracle artifacts are retired.
The five style keys a Designer export cannot carry now have one guard:
a unit test imports both default configurations and compares values, so a
mismatch names the key and both values instead of surfacing as an
unexplained diff hunk in the byte comparison.
…ry entry

Records why one gate replaced three, why tolerance is forbidden, why the
Oracle became a capability, and that the gate landed red on purpose — with
the condition that ends the red acceptance.
…actly

node scripts/verify.mjs builds both workspaces itself, captures a fresh Oracle
into a temp directory, renders the same fixture through the CLI, and compares
the outputs with strict string equality. Failures print a class-grouped
summary with counts and locations, the complete unified diff, and a hunk
total; both outputs and the diff land under a printed temp path, preserved
with --keep or --out-dir.

Exposed as the root wcli:verify script.
A minimal fixture (heading, paragraph, inline code) runs alongside the
full-coverage one by default, labelled as an infrastructure probe: its failure
means the harness is broken, its success proves nothing about fidelity.
--fixture restricts a run to one fixture.
--config takes an unmodified Designer export, keeps only render-affecting
keys, and feeds the same projected file to the CLI and to the browser (seeded
into the app's own storage before boot). Article content and pure-UI state are
dropped before either side sees them, and the seed is asserted to have taken
effect so a silent no-op cannot compare two site-default renders.

The five style keys an export cannot carry fall back to each side's own site
defaults, guarded by the site-default parity test.
…test

The gate now fails on pending placeholders (diagram, formula, unregistered
language) and on output that is not WeChat-ready (style block, class
attribute, block element without inline style), with the documented
structural exemptions. --self-test corrupts a known-good CLI render seven
distinct ways and asserts each mutant is rejected, without a browser.

Adapts the retired verifier's tests to the comparator rather than dropping
them.
CI installs Chromium, caching the browser directory keyed on the Playwright
version, and runs the verify gate so the diff is visible on every change
without failing the job while the gate is deliberately red. The three
existing jobs keep their coverage.
Both workflows stop calling the retired snapshot gate and run the same
pnpm wcli:verify as CI, with the same cached Chromium install, and stay
blocking: a red gate publishes nothing and holds the sync pull request. The
sync PR body renders its verification line from the run's actual outcome
instead of a hardcoded success.
One documentation area under packages/md-web-cli/docs: an entry document
covering install, usage, and verify invocation, the gate's own reference, the
relocated manual WeChat paste procedure, and the known blind spots (including
the missing diagram SVG pipeline and the untested real-device paste). The
package README points at it instead of duplicating it.
…acle scripts

With the verify gate holding the surviving assertions and the Oracle captured
at run time, the old machinery has no consumer left:

- the six oracle npm scripts, the four obsolete scripts, and the legacy
  rebuild wrapper (the capability lives in scripts/verify/oracle.mjs)
- the shared extraction helper that existed for tolerance and colour matching
- the golden snapshot and its refresh path
- the two oracle workflows
- the committed evidence directory and the standalone Oracle document
- the superseded tests; their coverage moved to the comparator's tests

Nothing regenerable is committed any more, and no live code, workflow, or doc
refers to the retired vocabulary.
…iew changes

--out-dir wrote both fixtures into one directory, so the second capture
overwrote the first. Artifacts now always land under <root>/<fixture>/.

Preview readiness compared element counts and lengths only, so a custom
fixture with the same shape as the app's default document could never look
ready; the signature now includes a hash of the text.
- share one repository path guard, and refuse an in-repo --out-dir before
  anything is written (previously projected-config.json/cli.html landed first)
- read the Oracle's completion signal from the clipboard instead of sleeping
  500ms past the copy button
- classify text-only drift as text rather than unclassified
- give the self-test a positive control: identical input must compare equal,
  or every reverse case would pass for the wrong reason
- run the browser-gated config-projection test in CI
- drop dead exports, dedupe the test subprocess helper, fix ADR/docs wording
  about seams, CI blocking, and the manual paste step
@DaneSpiritGOD DaneSpiritGOD added the do-not-merge Browse-only reference PR; must never be merged label Oct 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

do-not-merge Browse-only reference PR; must never be merged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant