From 27f5f84e3bfde84688f361aefaaa5c7d8489b1f4 Mon Sep 17 00:00:00 2001 From: Tianyi Cui <53024+tianyicui@users.noreply.github.com> Date: Thu, 18 Jun 2026 02:41:19 +0800 Subject: [PATCH] docs: address Codex review of the RFC reorg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix two root-AGENTS.md cross-links that the depth bump left pointing at the new docs/AGENTS.md instead of the root file they cite (capability-seams, optional-code-mode). These resolved on disk so verify-md-links passed — the gate checks existence, not which file you meant; corrected to ../../../. - Broaden verify-md-links scope to .agents/skills/**/*.md: this PR rewrote the dsh-code-review skill's links into the RFC tree, but the skill dir was outside the gate, so a broken skill link would have passed silently. - Percent-decode the path component before the existence check, so a valid encoded relative target (My%20File.md) is not falsely reported broken; a malformed escape (%zz) is reported broken rather than crashing the gate. - Drop the merged property-testing RFC's "nightly CI job 100x" claim: that line came from the original proposal, not the accepted decision, and CI has only push/pull_request triggers — note it as possible future work instead. doc-sync (incl. verify-md-links over 58 files), doc-typecheck, lint pass. --- .../2026-06-11-property-based-testing.md | 2 +- .../2026-06-13-capability-seams.md | 2 +- .../2026-06-18-markdown-cross-link-lint.md | 2 +- .../proposed/2026-06-15-optional-code-mode.md | 2 +- scripts/verify-md-links.ts | 44 +++++++++++++++---- 5 files changed, 40 insertions(+), 12 deletions(-) diff --git a/docs/rfc/implemented/2026-06-11-property-based-testing.md b/docs/rfc/implemented/2026-06-11-property-based-testing.md index ea6d76cc1c..9e4300cf65 100644 --- a/docs/rfc/implemented/2026-06-11-property-based-testing.md +++ b/docs/rfc/implemented/2026-06-11-property-based-testing.md @@ -12,7 +12,7 @@ Example-based tests pin the cases we thought of. The harness's core is protocol- ## Decision -Adopt `fast-check` (a root devDependency) with one `tests/properties.spec.ts` per protocol-shaped package, generators tuned for *realistic-but-adversarial* inputs (not uniform noise) and `numRuns` kept so the suite stays well under ~10s locally, with a nightly CI job running 100× the iterations. Failures print a reproducible seed. +Adopt `fast-check` (a root devDependency) with one `tests/properties.spec.ts` per protocol-shaped package, generators tuned for *realistic-but-adversarial* inputs (not uniform noise) and `numRuns` kept so the suite stays well under ~10s locally. Failures print a reproducible seed. (The original proposal also sketched a nightly CI job running 100× the iterations; that was not shipped — the property suite runs only in the normal `push`/`pull_request` CI, and a scheduled high-iteration job remains possible future work.) - **dsh-llm / BlockAssembler:** arbitrary chunk streams (valid + malformed: duplicate indices, stragglers, missing block-start). Invariants: `flushReady()+flushRemaining() ≡ blocks()` in order; the streamed prefix is always a prefix of final `blocks()`; partial count ≤ distinct indices; re-assembly idempotent. - **dsh-session:** arbitrary event logs. Invariants: `deriveMessages` deterministic; replay-from-seed identical; seq strictly monotonic; non-message events never affect derived history; derived content is decoupled from the log. diff --git a/docs/rfc/implemented/2026-06-13-capability-seams.md b/docs/rfc/implemented/2026-06-13-capability-seams.md index b04b35a5df..3446fce9af 100644 --- a/docs/rfc/implemented/2026-06-13-capability-seams.md +++ b/docs/rfc/implemented/2026-06-13-capability-seams.md @@ -26,4 +26,4 @@ The split is not mandatory when the parts are genuinely one concern: the LLM sea ## Consequences -More packages and more boilerplate per capability (a `package.json`/`tsconfig`/README trio, the inject wiring). Bought: implementations and consumers ship and version independently, and a new backend never risks the model-facing contract. The rule is documented in [AGENTS.md](../../AGENTS.md) § Conventions ("Capability seams are three packages") and [architecture.md](../../architecture.md) § "Capability seams"; the bash trio is the reference template. When to fold vs. split is a judgment call the architecture doc spells out — this RFC records *why* the default is to split. +More packages and more boilerplate per capability (a `package.json`/`tsconfig`/README trio, the inject wiring). Bought: implementations and consumers ship and version independently, and a new backend never risks the model-facing contract. The rule is documented in [AGENTS.md](../../../AGENTS.md) § Conventions ("Capability seams are three packages") and [architecture.md](../../architecture.md) § "Capability seams"; the bash trio is the reference template. When to fold vs. split is a judgment call the architecture doc spells out — this RFC records *why* the default is to split. diff --git a/docs/rfc/implemented/2026-06-18-markdown-cross-link-lint.md b/docs/rfc/implemented/2026-06-18-markdown-cross-link-lint.md index 1d015292f3..8bbef405f1 100644 --- a/docs/rfc/implemented/2026-06-18-markdown-cross-link-lint.md +++ b/docs/rfc/implemented/2026-06-18-markdown-cross-link-lint.md @@ -16,7 +16,7 @@ A fourth `doc-sync` gate, `verify-md-links` (`scripts/verify-md-links.ts`), mirr - Check a target only when it is a **relative path**. Skip scheme-qualified URLs (`https:`, `mailto:`, …), protocol-relative (`//host`), root-absolute (`/path` — no stable base in a checkout), and pure in-page anchors (`#section`). Strip any `#fragment`/`?query`, resolve the path against the linking file's directory, and assert it exists on disk. - Report and never rewrite; exit non-zero on the first broken link found. -Scope matches the other gates plus the AGENTS.md pair: `README.md`, `docs/**/*.md`, `packages/*/README.md`, `AGENTS.md`, `packages/AGENTS.md`, deduped by real path (the `CLAUDE.md` symlinks resolve onto the AGENTS.md files). It is wired into the `doc-sync` script that the lefthook pre-push hook and CI both run, so a broken link fails locally before a push — consistent with [mechanical quality gates](2026-06-11-quality-gates.md). +Scope matches the other gates plus the AGENTS.md pair and the repo-authored agent-skill Markdown under `.agents/skills/` (those skill files cross-link into the docs tree, so this reorg rewrote links in them too): `README.md`, `docs/**/*.md`, `packages/*/README.md`, `AGENTS.md`, `packages/AGENTS.md`, `.agents/skills/**/*.md`, deduped by real path (the `CLAUDE.md` symlinks resolve onto the AGENTS.md files). It is wired into the `doc-sync` script that the lefthook pre-push hook and CI both run, so a broken link fails locally before a push — consistent with [mechanical quality gates](2026-06-11-quality-gates.md). This gate checks *existence*, not anchor validity: a link to a real file with a `#wrong-heading` fragment still passes (the file resolves; the fragment is stripped). Anchor-level checking is a heavier, lower-value follow-up — file-level dead links are the failure that actually bit us. diff --git a/docs/rfc/proposed/2026-06-15-optional-code-mode.md b/docs/rfc/proposed/2026-06-15-optional-code-mode.md index f182b585c5..598986a521 100644 --- a/docs/rfc/proposed/2026-06-15-optional-code-mode.md +++ b/docs/rfc/proposed/2026-06-15-optional-code-mode.md @@ -38,7 +38,7 @@ Per the "explicit > implicit at seams" convention, the request spells out every These are illustrations of the seam's reach, **not commitments** — the MVP ships only the TypeScript path. The honest caveat is that the *execution* contract is language-agnostic but the *presentation* is not: the SDK-generation pipeline below (§3a and the `jsonSchemaToTs` codegen, which emits a TypeScript `.d.ts`) is TypeScript-specific, so a non-TS backend pairs the shared `CodeRuntime` contract with its own language-appropriate SDK generator and system-prompt section (a `.pyi` stub and Python usage instructions for the Python backend, AssemblyScript-flavored types for that one). The runtime seam is reused as-is; only the codegen/prompt half is per-language. -**2. Implementation package `packages/code-runtime-vm/`** — a new package `@deepseek-ai/dsh-code-runtime-vm`, the `node:vm` reference stub. It type-erases the model's TypeScript via the compiler's `transpileModule` (or sucrase) — the types exist only to guide the model; the runtime is plain JS — then wraps the body in an async IIFE for top-level `await` (Cloudflare's `NodeVMExecutor` does literally `new AsyncFunction("codemode", "return await (${code})()")`), runs it in a `vm.Context` whose globals are a capturing `console` and the SDK namespace objects, awaits the IIFE, and captures the return value, the buffered logs, and any thrown error (as `error: string`). It applies an **output cap** (truncate captured logs) and a **timeout tied to `request.signal`**. These caps limit blast radius; **they are not a security boundary**. node:vm is **not** isolation: withholding `require`/`process` does not contain anything (code escapes via `constructor`/prototype reflection), and per [AGENTS.md](../../AGENTS.md) the harness must never hand model output the ambient environment. +**2. Implementation package `packages/code-runtime-vm/`** — a new package `@deepseek-ai/dsh-code-runtime-vm`, the `node:vm` reference stub. It type-erases the model's TypeScript via the compiler's `transpileModule` (or sucrase) — the types exist only to guide the model; the runtime is plain JS — then wraps the body in an async IIFE for top-level `await` (Cloudflare's `NodeVMExecutor` does literally `new AsyncFunction("codemode", "return await (${code})()")`), runs it in a `vm.Context` whose globals are a capturing `console` and the SDK namespace objects, awaits the IIFE, and captures the return value, the buffered logs, and any thrown error (as `error: string`). It applies an **output cap** (truncate captured logs) and a **timeout tied to `request.signal`**. These caps limit blast radius; **they are not a security boundary**. node:vm is **not** isolation: withholding `require`/`process` does not contain anything (code escapes via `constructor`/prototype reflection), and per [AGENTS.md](../../../AGENTS.md) the harness must never hand model output the ambient environment. **The unsafe-runtime guard is enforceable, not a README warning.** Because a README caveat is not a control — and AGENTS.md's "never hand model output ambient authority" is a hard rule, not advice — the design makes the danger refuse to run by construction. Two layers: diff --git a/scripts/verify-md-links.ts b/scripts/verify-md-links.ts index 4120b02c6e..35e42452ca 100644 --- a/scripts/verify-md-links.ts +++ b/scripts/verify-md-links.ts @@ -20,10 +20,13 @@ * resolved against the linking file's directory, and the result must exist on * disk. This is checker, not fixer: it reports and never rewrites. * - * Scope mirrors the other doc-sync gates plus the two AGENTS.md files: - * README.md, docs/** /*.md, packages/* /README.md, AGENTS.md, packages/AGENTS.md. - * The root and packages/ CLAUDE.md are symlinks to the AGENTS.md files, so they - * are deduped by real path. + * Scope is the other doc-sync gates' set plus the two AGENTS.md files AND the + * repo-authored agent-skill Markdown under `.agents/skills/` — those skill + * files cross-link into the docs tree (e.g. the dsh-code-review skill cites the + * RFC index), so a rename must not silently break them either: README.md, + * docs/** /*.md, packages/* /README.md, AGENTS.md, packages/AGENTS.md, + * .agents/skills/** /*.md. The root and packages/ CLAUDE.md are symlinks to the + * AGENTS.md files, so they are deduped by real path. * * Run: `tsx scripts/verify-md-links.ts`. */ @@ -38,8 +41,18 @@ import type { Nodes } from 'mdast' const root = resolve(import.meta.dirname, '..') -/** Files to check: doc-typecheck's scope plus the AGENTS.md pair. */ -const PATTERNS = ['README.md', 'docs/**/*.md', 'packages/*/README.md', 'AGENTS.md', 'packages/AGENTS.md'] +/** + * Files to check: doc-typecheck's scope, the AGENTS.md pair, and repo-authored + * agent-skill Markdown (which this repo's own docs reorg rewrites links in). + */ +const PATTERNS = [ + 'README.md', + 'docs/**/*.md', + 'packages/*/README.md', + 'AGENTS.md', + 'packages/AGENTS.md', + '.agents/skills/**/*.md', +] /** A broken relative link: a target path that does not resolve to a file. */ interface Violation { @@ -62,9 +75,24 @@ function isExternalOrAnchor(url: string): boolean { return /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(url) } -/** Strip the `#fragment` and `?query` from a link target, leaving the path. */ +/** + * Strip the `#fragment` and `?query` from a link target, then percent-decode + * the remaining path so an encoded target (`My%20File.md`, `READ%4DE.md`) + * probes the real filename on disk, the way a Markdown renderer resolves it. A + * malformed escape (`%zz`) makes `decodeURIComponent` throw; we keep the raw + * path in that case so the link is reported as broken (a `%zz` target is not a + * file anyone meant to link) rather than crashing the gate. + */ function pathPart(url: string): string { - return url.replace(/[#?].*$/, '') + const raw = url.replace(/[#?].*$/, '') + try { + return decodeURIComponent(raw) + } catch { + // decodeURIComponent throws only on a malformed percent-escape; the raw + // string is then a path no renderer resolves, so fall through to the + // existence check, which reports it broken. + return raw + } } /** Find every broken relative cross-link in one Markdown file via its AST. */