Skip to content

Latest commit

 

History

History
71 lines (59 loc) · 15.1 KB

File metadata and controls

71 lines (59 loc) · 15.1 KB

Shojiku

AI-only: token-dense repo map for coding agents; humans start at docs/README.md.

PDF document engine (invoices/slips): template YAML + params JSON → layout → PDF. Pipeline: Template/Definitions → Bundle → Layout → Render → Preview → Sign → Verify → Archive.

Code map — read the touched area's map BEFORE searching

The file-by-file repo map is split per component under docs/code-map/ (AI-only, token-dense; entry granularity + maintenance rules: docs/code-map/README.md). Read the map file for every directory you are about to search or edit — it is cheaper than searching cold. Seam work (the wasm boundary, the GUI↔engine wire) reads BOTH sides' maps.

Rust workspace lives in engine/ (crates named shojiku-<dir>). Every .rs file opens with a //! role header, so head -1 identifies a file without opening it; unit tests live in #[cfg(test)]-only sibling files (<mod>/tests.rs, further split under <mod>/tests/), near-e2e suites in the crate's tests/ directory.

You touch Read first It carries
engine/core, engine/diagnostics docs/code-map/core.md template/definitions/params wire model, two-pass parse, validation walks, the diagnostics code registry
engine/layout, engine/layout-box docs/code-map/layout.md positioning/pagination, text·wrap·fonts, the layout tree (the ONLY layout↔renderer contract), the box index, the e2e suite map
engine/formatter, engine/image docs/code-map/formatter-image.md display strings, locale packs (CLDR builtins, 和暦), the font-pack wire; asset policy/SVG/raster
engine/render-pdf, engine/render-png docs/code-map/render.md the two draw backends (krilla / tiny-skia) over the layout tree
engine/signing docs/code-map/signing.md the PDF incremental-update writer (tail/xref/object reading under the hostile-input posture, revision append, the signature placeholder + byte ranges) AND the signer: PKCS#8 key loading, CMS SignedData, the prepare_sign/complete_sign split; the shared PDF model + OID table engine/verify reads through
engine/verify, engine/fuzz docs/code-map/verify.md the verifier: the structural walk to a signature, the byte-range COVERAGE rule (a valid signature over an incomplete range is a forgery), CMS/certificate checking, and the report that states what it did NOT check (seam work reads signing.md too); plus the out-of-workspace libFuzzer crate and its corpus-replay contract
engine/authoring, engine/cli, engine/mcp, engine/wasm, engine/capi, engine/napi, engine/fetch docs/code-map/hosts.md the ONE bytes-first authoring surface + its five thin hosts (CLI / MCP stdio / browser WASM / C ABI cdylib the FFI SDKs load / the N-API addon the npm package loads, which reaches the engine THROUGH the cdylib host rather than beside it) + the host-only font fetch crate, capability keys
gui/designer-core docs/code-map/gui-core.md headless document model (parse/serialize, patch ops, editor session) + the workspace/toolchain preamble
gui/designer docs/code-map/gui-designer.mdsplit by area, read the one you touch: canvas (engine transport, preview loop, paint/overlay/zoom, drag·resize·snap) · panel (property panel, placement, borders, page setup, styles, columns, diagnostics) · insert (insert menu/dialogs, scaffolds, image·paste import, field palette, sample data, data-item editor) · chrome (menubar, toolbar, help, i18n, ui/ primitives, theme/CSS, chip text editor) · tutorial (step data, coach mark, launcher, practice-document swap) the embeddable React component; the index file itself carries the assembled Designer, layer tree, sidebar, editor session, the ShojikuGui hook registry and test substrate
gui/designer-app docs/code-map/gui-app.md the standalone app host: services seam, presets/fonts/persistence, build/assemble, integration suites (Designer work spanning packages reads the neighbor's map too)
sdk/ docs/code-map/sdk.md the seven language wrappers, all built — the ruby gem (the REFERENCE the other six mirror: result/trace shape, template-root hardening, the fiddle ownership rules), the python package mirroring it over ctypes, the .NET package over function pointers and the JVM one over JNA, the npm package over the N-API addon, and the two SUBPROCESS ones (php, go) that script the CLI instead, plus their gate containers
packs/, examples/, docker/, docs/, site/, skills/, scripts/, mk/, .claude/hooks/ docs/code-map/repo.md font/locale packs, bundled examples (+ the output-refresh rule), runtime image, doc set, homepage pitch pages, product-facing AI skills (npx skills add layout), repo gate scripts, the per-scope Makefile includes, the tracked Claude Code hooks (the deterministic half of the development rules)

The other half of the map: the rulebooks

The table above answers WHERE — which files own the thing you are changing. These answer HOW, and each is the single canonical home for its concern (component docs reference them rather than restating):

You need Read
where a feature BELONGS; which boundary must not move docs/architecture.md (the constitution) + docs/agents/<area>.md for the component
the style / lint / 100%-coverage bar, in any language docs/guidelines.md
how correctness is ESTABLISHED at all — and why you may not invent a check docs/agents/verification.md + the allowlist at head -30 Makefile
a gate failed and you do not recognise the failure the FAIL block itself — it names the TREE it ran over, WHERE it broke and WHAT IT IS, and prints the command that fixes it; kept at .make-logs/last-error.log. The follow-up questions are commands too: make investigate:tree / :docker / :gates / :coverage / :render / :pins (make help)
what is BUILT vs merely DECIDED docs/engine/features.md vs docs/agents/<area>.md

Working order for a change: code map → rulebooks → build → the verification rulebook says whether it worked. The forward-looking queue (what to work on) is deliberately NOT in this repository.

Rules that bite

  • No local cargo: run gates via make (Docker wrapper). The merge bar is CI, not a local full run (user decision): make verify still exists and is still the whole mirror (line budget, fmt, clippy -D warnings, test, coverage 100% lines blocking, cargo-deny, the key-catalog + reference-table drift gate (make reference:check; regenerate via make reference:generate, which rewrites both the catalog and the generated tables in docs/engine/*.md), examples output-hash check (make examples:check; refresh via make examples:render), docker build/render/trivy), but it is NOT the routine pre-PR step — CI runs a strict superset of it (see below) in parallel, in minutes rather than the ~20 a local serial run costs. Reach for it only when you cannot use CI: working offline, or changing a gate's own recipe and wanting the answer before pushing.
  • Checking a result ≠ reading output. To check, use the <scope>:<job> grid — make engine:verify / gui:verify / site:verify / docker:verify, and the faster slices engine:budget / engine:lint / engine:test (same shape for gui: and site:; make help lists them all). Each prints ONE PASS/FAIL line, exits with the gate's REAL code, and keeps the full log in .make-logs/. A quiet failure always lands at the fixed path .make-logs/last-error.log (headed with the target, exit code and the last == step == reached, cleared when that target next passes) — cat it instead of re-running to find where it broke. V=1 streams the raw output INSTEAD: it bypasses the quiet wrapper, so it writes no log and runs neither gate-culprits.sh nor gate-diagnose.sh. Reach for it when you are reading a failure you already have, not when you are asking whether something passed. A correctness claim comes from a sanctioned make target — never invent an equivalent (user rule): the sanctioned commands are the allowlist at the top of the Makefile (head -30 Makefile), and the rulebook behind it — what counts as a claim vs mere inspection, never wrapping a gate in a pipe / ; echo $? / make -n, what to do when the command you need is missing, and when a green verify still stands — is docs/agents/verification.md. Read it before saying anything works. Every job has exactly ONE name and is quiet by default; add V=1 to any of them (make gui:verify V=1) for the raw output while debugging a failure. make make:check is the gate over the surface itself — it refuses a target filed under the wrong mk/<scope>.mk, and any tracked file naming a target that does not exist (a doc placeholder like <scope>:lock, and a CI matrix's ${{ }} name, are checked as patterns).
  • CI runs the same make targets you do (.github/workflows/ci.yml), in the same pinned containers, as ~30 parallel checks (14 job definitions; the SDK and install-proof matrices expand them) — engine, gui, wasm, site, docker, and every SDK across its supported language versions. There is no second definition of a gate to keep in sync. It is a strict superset of make verify: every one of that target's prerequisites has a CI job (the engine-cache composite runs make <target>), and CI additionally runs site:build, the SDK version MATRIX (local runs one version per language) and the proof:<lang> install checks. So a green CI is a stronger statement than a green local mirror, never a weaker one. It fires on pull_request, so the PR is what starts it — a feature-branch push alone runs nothing.
  • main takes no force pushes and cannot be deleted (repository ruleset); history accumulates through PRs. A push or PR starts CI normally — the exception is a change whose every path matches ci.yml's paths-ignore (**/*.md, docs/**, .gitignore, LICENSE*), where no ci.yml job runs at all. gh workflow run ci.yml --ref main is the manual trigger for that case and for re-running a gate without a new commit. Three of those ignored paths are not inert: docs/engine/** is projected onto the site, where the page set, the per-page reference: front-matter and the link round-trip are all asserted, CHANGELOG.md decides which engine versions the site may pin, and README.md feeds the generated gallery — so site-docs.yml runs make site:verify over exactly those three (one job, no Rust, no wasm). A path filter is evaluated against the EVENT, so a commit-message marker can only ever SKIP a run ([skip ci]), never start one.
  • GNU Make 4 or newer. make is the only tool that does not run in a container, so it is the one place local and CI can disagree; macOS ships 3.81, which parses recipe quoting differently. The Makefile refuses (everything but help) and names the fix.
  • Line budget: WHY there is a cap, and why hitting one is a signal about the design rather than an instruction to trim, is docs/guidelines.md § File length — read it before splitting anything. Every non-test .rs under engine/ is ≤300 lines (≤160 is the design target for a NEW file, and is not gated) and every .rs, tests included, must start with a //! role header — make engine:budget (scripts/check-line-budget.sh) gates both in CI; exceeding 300 needs an in-file line-budget-exempt: <reason> waiver. clippy::too_many_lines (threshold 150, engine/clippy.toml) gates function length. The gui side has the same shape: every non-test .ts/.tsx under gui/ is ≤150 executable lines (blank lines and comments excluded, so documenting a file costs no budget) via make gui:budget (scripts/check-gui-line-budget.sh, the first step of make gui:verify), same line-budget-exempt: <reason> waiver token; Biome's noExcessiveLinesPerFunction (150, gui/biome.json) gates function length there with no waiver list. Test files are outside the length budget on both sides — a suite is a list of independent cases, not a unit of design.
  • Test placement (canonical: docs/guidelines.md § Where tests live): focused unit tests go in #[cfg(test)] mod tests; sibling files (<mod>/tests.rs), NOT the Book's inline mod tests { … }. A grown suite splits under <mod>/tests/, where the directory names the target and the file names the aspect — and every split suite's //! header says what it covers, as a behaviour where the suite drives a public entry point (most of them) and as a source module only where it is genuinely scoped to one. There is no file-to-file mapping between a module's sources and its suites, and chasing one costs what the names carry. Near-e2e suites go in the crate's tests/ as one binary (tests/<name>/main.rs + modules). The coverage bar is the workspace run (make engine:coverage, 100% lines blocking); per-crate cargo llvm-cov -p <crate> is a diagnostic, not a gate.
  • engine/Cargo.lock is committed and stages normally (git add); the one deliberately gitignored lockfile is engine/fuzz/Cargo.lock. (An old rule said the global gitignore hides it and -f is needed — that was a misread of *.log in the global excludes, and a tracked file's changes never need -f anyway.)
  • deny.toml has zero advisory ignores; don't add deps that break that (e.g. anything pulling ttf-parser or lopdf).
  • Renderers never re-measure/re-format; layout never draws. Locale data → packs/locale/, fonts → packs/fonts/, business rules → future plugins/, never into engine crates.
  • Coverage counts each crate twice (its own unit-test binary + the copy linked into dependents' test binaries); a line only covered in one copy can still fail the 100% gate — cover new code in the crate's own unit tests first.
  • The roadmap is not in this repository. docs/agents/<area>.md states what is DECIDED and docs/engine/features.md what is BUILT; anything absent from both is unsettled, so propose it rather than assuming it was rejected. When something ships, its substance goes to docs/engine/features.md + the docs/engine/ reference (the implemented-capability list + decision log).
  • Architecture/policy docs: docs/architecture.md, docs/engine/features.md (what's built), docs/engine/ (authorable-syntax reference, one page per feature), docs/agents/<area>.md, docs/guidelines.md.
  • The code map (docs/code-map/, routed by the table above) is the token-saving entry point: read the touched component's file BEFORE searching, and update it in the same PR whenever crates/modules/boundaries change.