Put the developer docs under docs/dev and index the folder for users first
docs/ had 26 developer documents flat beside the manual, and the two audiences are very differently sized: most readers want the manual and the gesture reference, a few want the register, the designs and the measurements. The manual and gestures.md stay at the top; everything for someone changing the code moves to docs/dev/, and the two documents that name their own successors — the v0.1 milestone and the UI-refinement plan — go to docs/dev/archive/ rather than being deleted, since both are still cited. docs/README.md is the index, users first. Every reference follows: code comments, Cargo manifests, the workflows, the pre-commit hook, the bench and traceability tools (which locate the repo root by docs/dev/requirements.md now), packaging, the Docker READMEs, CLAUDE.md, CONTRIBUTING.md and the README. The matrix links one level deeper and is regenerated. Links out of the moved documents into the tree gain a level; a link checker over every Markdown file finds none broken.
This commit is contained in:
@@ -13,7 +13,7 @@ license.workspace = true
|
||||
# into a job that has no adapter to use them with, and adding `dr-ui` would
|
||||
# pull Slint. The GPU half of the suite is `dr-gpu`'s own frame-budget test,
|
||||
# which already exists and already skips itself where there is no device — see
|
||||
# `docs/benchmarks.md`.
|
||||
# `docs/dev/benchmarks.md`.
|
||||
[dependencies]
|
||||
dr-types.workspace = true
|
||||
dr-catalog.workspace = true
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! The committed numbers, and what counts as a regression against them.
|
||||
//!
|
||||
//! `docs/frame-budget.md` commits its measurements by hand and says why: *"a
|
||||
//! `docs/dev/frame-budget.md` commits its measurements by hand and says why: *"a
|
||||
//! regression should be a diff rather than somebody's memory."* This is the
|
||||
//! same idea in a form a program can read, because §8 asks for more than a
|
||||
//! record — *"a regression beyond stated tolerance fails the build"*.
|
||||
@@ -80,7 +80,7 @@ pub struct Metric {
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Baseline {
|
||||
/// Prose for whoever opens the JSON first. Kept in the file rather than
|
||||
/// only in `docs/benchmarks.md`, because the person who finds this in a
|
||||
/// only in `docs/dev/benchmarks.md`, because the person who finds this in a
|
||||
/// failing CI log is not reading the docs directory at that moment.
|
||||
#[serde(rename = "_readme")]
|
||||
pub readme: Vec<String>,
|
||||
@@ -173,15 +173,15 @@ impl Baseline {
|
||||
|
||||
/// Where the committed baseline lives, found the way `tools/traceability`
|
||||
/// finds the repo root: by walking up from this crate's manifest until
|
||||
/// `docs/requirements.md` appears.
|
||||
/// `docs/dev/requirements.md` appears.
|
||||
pub fn default_path() -> Result<PathBuf> {
|
||||
let mut dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
while !dir.join("docs/requirements.md").exists() {
|
||||
while !dir.join("docs/dev/requirements.md").exists() {
|
||||
if !dir.pop() {
|
||||
anyhow::bail!("could not locate the repo root above this crate");
|
||||
}
|
||||
}
|
||||
Ok(dir.join("docs/bench-baseline.json"))
|
||||
Ok(dir.join("docs/dev/bench-baseline.json"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
//!
|
||||
//! That is a deliberate refusal rather than an oversight. `CONTRIBUTING.md`
|
||||
//! asks that a requirement be closed by a test that would fail if the
|
||||
//! behaviour were removed, and `docs/code-health.md` CH-4 records what the
|
||||
//! behaviour were removed, and `docs/dev/code-health.md` CH-4 records what the
|
||||
//! coverage figure looks like when tags are hung on plumbing instead. A tag
|
||||
//! here would say the export budget is checked; the GPU half of it would still
|
||||
//! be unchecked.
|
||||
@@ -26,7 +26,7 @@
|
||||
//! It is a **one-sided** gate, and that is worth having. The encode half is a
|
||||
//! lower bound on the whole: if resizing, sharpening and encoding 24 MP alone
|
||||
//! take longer than two seconds, NFR-P7 is violated no matter how fast the
|
||||
//! render is. So the budget in `docs/bench-baseline.json` is the requirement's
|
||||
//! render is. So the budget in `docs/dev/bench-baseline.json` is the requirement's
|
||||
//! own 2000 ms, and exceeding it fails the build honestly. Coming in under it
|
||||
//! proves nothing about the requirement, and the report says so rather than
|
||||
//! printing a tick.
|
||||
@@ -64,7 +64,7 @@ const RUNS: usize = 5;
|
||||
|
||||
/// A row of the export table.
|
||||
pub struct EncodeRun {
|
||||
/// The name this row carries in `docs/bench-baseline.json`.
|
||||
/// The name this row carries in `docs/dev/bench-baseline.json`.
|
||||
pub key: &'static str,
|
||||
pub label: &'static str,
|
||||
pub width: u32,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! The synthetic 50k catalog, and the handful of real files it points at.
|
||||
//!
|
||||
//! `docs/requirements.md` §8 asks for "an automated benchmark suite against a
|
||||
//! `docs/dev/requirements.md` §8 asks for "an automated benchmark suite against a
|
||||
//! synthetic 50k catalog". The hard part of that sentence is *50k*: a real
|
||||
//! library of that size is several terabytes and cannot live in a repository,
|
||||
//! in a CI cache, or on a laptop that also has to compile the thing.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! The benchmark suite `docs/requirements.md` §8 has been promising.
|
||||
//! The benchmark suite `docs/dev/requirements.md` §8 has been promising.
|
||||
//!
|
||||
//! §8 says performance is verified by *"an automated benchmark suite against a
|
||||
//! synthetic 50k catalog, run per-commit … A regression beyond stated
|
||||
@@ -191,17 +191,17 @@ enum Mode {
|
||||
fn print_help() {
|
||||
println!(
|
||||
"\
|
||||
dr-bench — DarkRoom's performance suite (docs/requirements.md §8)
|
||||
dr-bench — DarkRoom's performance suite (docs/dev/requirements.md §8)
|
||||
|
||||
run measure and print, judging nothing
|
||||
check measure, print, and exit 1 on a violated budget or a regression
|
||||
record measure and rewrite docs/bench-baseline.json from the result
|
||||
record measure and rewrite docs/dev/bench-baseline.json from the result
|
||||
|
||||
Flags:
|
||||
--reference this machine is the reference desktop, so machine-sensitive
|
||||
budgets are asserted rather than reported
|
||||
--fixture <dir> where the synthetic 50k catalog lives (or $DR_BENCH_DIR)
|
||||
--baseline <file> the committed numbers (default docs/bench-baseline.json)
|
||||
--baseline <file> the committed numbers (default docs/dev/bench-baseline.json)
|
||||
--lanes <n> sweep lanes for the thumbnail row (default: CPU threads)
|
||||
--thumbnails <n> images in the thumbnail row (default 1200)
|
||||
|
||||
@@ -307,8 +307,8 @@ fn measure_and_report(
|
||||
}
|
||||
println!();
|
||||
println!(
|
||||
" {} gate(s) failed. docs/benchmarks.md says what each metric measures and\n \
|
||||
docs/bench-baseline.json holds the numbers these used to be.",
|
||||
" {} gate(s) failed. docs/dev/benchmarks.md says what each metric measures and\n \
|
||||
docs/dev/bench-baseline.json holds the numbers these used to be.",
|
||||
failures.len()
|
||||
);
|
||||
Ok(mode != Mode::Gate)
|
||||
@@ -453,7 +453,7 @@ fn print_verdict(
|
||||
machine: &str,
|
||||
comparable_fixture: bool,
|
||||
) -> Vec<String> {
|
||||
println!("Against docs/bench-baseline.json");
|
||||
println!("Against docs/dev/bench-baseline.json");
|
||||
match (&base.recorded_on, cx.same_machine) {
|
||||
(None, _) => println!(
|
||||
" No baseline has been recorded yet. Budgets are still gated; drift is not.\n \
|
||||
|
||||
+6
-6
@@ -180,7 +180,7 @@ job_traceability() {
|
||||
|
||||
# Compared against the working tree, not against HEAD.
|
||||
#
|
||||
# CI asks `git diff --quiet docs/traceability.md` after regenerating, which
|
||||
# CI asks `git diff --quiet docs/dev/traceability.md` after regenerating, which
|
||||
# is right there and wrong here: CI starts from a clean checkout, so the
|
||||
# only diff it can see is the one regeneration introduced. Locally the file
|
||||
# is usually already modified for honest reasons — an uncommitted feature
|
||||
@@ -191,14 +191,14 @@ job_traceability() {
|
||||
step "traceability: matrix is up to date"
|
||||
local before
|
||||
before="$(mktemp)"
|
||||
cp docs/traceability.md "${before}"
|
||||
cp docs/dev/traceability.md "${before}"
|
||||
cargo run -q -p traceability -- report
|
||||
if diff -q "${before}" docs/traceability.md >/dev/null; then
|
||||
if diff -q "${before}" docs/dev/traceability.md >/dev/null; then
|
||||
record "traceability/matrix" 0
|
||||
else
|
||||
echo "docs/traceability.md was stale; regenerating changed it:"
|
||||
diff --stat "${before}" docs/traceability.md 2>/dev/null \
|
||||
|| diff "${before}" docs/traceability.md | head -20
|
||||
echo "docs/dev/traceability.md was stale; regenerating changed it:"
|
||||
diff --stat "${before}" docs/dev/traceability.md 2>/dev/null \
|
||||
|| diff "${before}" docs/dev/traceability.md | head -20
|
||||
record "traceability/matrix" 1
|
||||
fi
|
||||
rm -f "${before}"
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
# ./tools/export-migan.sh # -> models/inpaint/migan-512.onnx
|
||||
#
|
||||
# Since 2026-09-20 the file that ships is not this export but a fine-tune of
|
||||
# it on panorama-border voids (docs/panorama.md §14), made in the
|
||||
# it on panorama-border voids (docs/dev/panorama.md §14), made in the
|
||||
# `darkroom-infill` repository with `python -m infill.export`. This script
|
||||
# still yields the stock generator — the fine-tune's starting point, and the
|
||||
# model the page's "mirror depth" knob above zero was built around.
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#
|
||||
# ## Why this exists
|
||||
#
|
||||
# `dr-face`'s similarity scan picks its dot product per machine (docs/faces.md
|
||||
# `dr-face`'s similarity scan picks its dot product per machine (docs/dev/faces.md
|
||||
# §9): AVX2 where the CPU has it, **NEON on aarch64**, and a portable loop
|
||||
# otherwise. The NEON kernel is the one that runs on the phone and the tablet,
|
||||
# and it is the one a desktop `cargo test` never executes — a wrong lane index
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
# Fetch the inference runtime the Android APK carries (docs/inference.md §3).
|
||||
# Fetch the inference runtime the Android APK carries (docs/dev/inference.md §3).
|
||||
#
|
||||
# ./tools/fetch-android-runtime.sh [DEST]
|
||||
#
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# Put a GPU-capable ONNX Runtime where the desktop app looks for one
|
||||
# (docs/inference.md §3): `runtime/` beside the models in the user data
|
||||
# (docs/dev/inference.md §3): `runtime/` beside the models in the user data
|
||||
# directory, ahead of the system library.
|
||||
#
|
||||
# ./tools/fetch-desktop-runtime.sh [DEST]
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
# ./tools/fix-face-model-shapes.sh IN.onnx OUT.onnx --input NAME=1,3,640,640
|
||||
# ./tools/fix-face-model-shapes.sh IN.onnx OUT.onnx --dim NAME=1
|
||||
#
|
||||
# The two the face pipeline needs, verified 2026-08-26 (docs/faces.md §12 M1):
|
||||
# The two the face pipeline needs, verified 2026-08-26 (docs/dev/faces.md §12 M1):
|
||||
#
|
||||
# ... det_500m.onnx scrfd_500m_640.onnx --input input.1=1,3,640,640
|
||||
# ... det_2.5g.onnx scrfd_2.5g_640.onnx --input input.1=1,3,640,640
|
||||
# ... det_10g.onnx scrfd_10g_640.onnx --input input.1=1,3,640,640
|
||||
# ... w600k_mbf.onnx arcface_mbf_b1.onnx --dim None=1
|
||||
#
|
||||
# And the three eye-state models, verified 2026-09-19 (docs/faces.md §17).
|
||||
# And the three eye-state models, verified 2026-09-19 (docs/dev/faces.md §17).
|
||||
# The landmark model's batch is the literal "None" like the embedder's; the
|
||||
# two classifiers' is a *named* dim_param "batch":
|
||||
#
|
||||
@@ -43,7 +43,7 @@
|
||||
# ## Why it is a script and not a build step
|
||||
#
|
||||
# Same reason as the segmentation export: the model is not a build input
|
||||
# (docs/faces.md §2.2 — the weights are never committed, because InsightFace's
|
||||
# (docs/dev/faces.md §2.2 — the weights are never committed, because InsightFace's
|
||||
# grant is non-commercial). This runs once, wherever the user's model lives,
|
||||
# and the app loads the result. It exists so the transformation is reproducible
|
||||
# rather than a binary someone once produced and nobody can regenerate.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
# Produce the int8 form of a model for the Hexagon (docs/inference.md §5).
|
||||
# Produce the int8 form of a model for the Hexagon (docs/dev/inference.md §5).
|
||||
#
|
||||
# ./tools/quantise-models.sh PHOTO_DIR MODEL.onnx [MODEL.onnx ...]
|
||||
#
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Traceability gate and matrix generator.
|
||||
//!
|
||||
//! ```text
|
||||
//! traces report # write docs/traceability.md
|
||||
//! traces report # write docs/dev/traceability.md
|
||||
//! traces json # machine-readable, to stdout
|
||||
//! traces check # gate: non-zero exit on failure
|
||||
//! traces gestures # write docs/gestures.md and the app's gesture table
|
||||
@@ -67,7 +67,7 @@ fn main() -> Result<()> {
|
||||
return run_gestures(&base, mode == "gestures-check");
|
||||
}
|
||||
|
||||
let req_path = base.join("docs/requirements.md");
|
||||
let req_path = base.join("docs/dev/requirements.md");
|
||||
let markdown = std::fs::read_to_string(&req_path)
|
||||
.with_context(|| format!("reading {}", req_path.display()))?;
|
||||
let defined = parse_defined_requirements(&markdown);
|
||||
@@ -106,7 +106,7 @@ fn main() -> Result<()> {
|
||||
println!("\ntraceability gate: PASS");
|
||||
}
|
||||
_ => {
|
||||
let out = base.join("docs/traceability.md");
|
||||
let out = base.join("docs/dev/traceability.md");
|
||||
let md = render(&files, &entries, &defined, &coverage);
|
||||
std::fs::write(&out, md)?;
|
||||
print_summary(&files, &entries, &defined, &coverage);
|
||||
@@ -195,7 +195,7 @@ fn run_gestures(base: &Path, check: bool) -> Result<()> {
|
||||
fn gate(files: &[PathBuf], defined: &DefinedRequirements, cov: &Coverage) -> Result<()> {
|
||||
// A run that parsed nothing is misconfigured, not passing.
|
||||
if defined.total() == 0 {
|
||||
bail!("no requirements parsed from docs/requirements.md — misconfigured, not 0% coverage");
|
||||
bail!("no requirements parsed from docs/dev/requirements.md — misconfigured, not 0% coverage");
|
||||
}
|
||||
if files.is_empty() {
|
||||
bail!("no source files scanned — misconfigured, not 0% coverage");
|
||||
@@ -325,7 +325,7 @@ fn render(
|
||||
for (id, es) in &by_req {
|
||||
let mut locs: Vec<String> = es
|
||||
.iter()
|
||||
.map(|e| format!("[`{}:{}`](../{}#L{})", e.file, e.line, e.file, e.line))
|
||||
.map(|e| format!("[`{}:{}`](../../{}#L{})", e.file, e.line, e.file, e.line))
|
||||
.collect();
|
||||
locs.sort();
|
||||
locs.dedup();
|
||||
@@ -346,7 +346,7 @@ fn render(
|
||||
let mut locs: Vec<String> = entries
|
||||
.iter()
|
||||
.filter(|e| e.requirements.contains(id))
|
||||
.map(|e| format!("[`{}:{}`](../{}#L{})", e.file, e.line, e.file, e.line))
|
||||
.map(|e| format!("[`{}:{}`](../../{}#L{})", e.file, e.line, e.file, e.line))
|
||||
.collect();
|
||||
locs.sort();
|
||||
locs.dedup();
|
||||
@@ -379,9 +379,9 @@ fn render(
|
||||
/// The repo root, found by walking up from the executable's manifest dir.
|
||||
fn repo_root() -> Result<PathBuf> {
|
||||
let mut dir = Path::new(env!("CARGO_MANIFEST_DIR")).to_path_buf();
|
||||
while !dir.join("docs/requirements.md").exists() {
|
||||
while !dir.join("docs/dev/requirements.md").exists() {
|
||||
if !dir.pop() {
|
||||
bail!("could not locate repo root (no docs/requirements.md above the tool)");
|
||||
bail!("could not locate repo root (no docs/dev/requirements.md above the tool)");
|
||||
}
|
||||
}
|
||||
Ok(dir)
|
||||
|
||||
Reference in New Issue
Block a user