From 4b0206b396aed2afedb66463973bb7b656297925 Mon Sep 17 00:00:00 2001 From: Orual Date: Sat, 1 Aug 2026 14:49:10 -0400 Subject: [PATCH] GC Unit D: resolve Python cache ignore rules --- AGENTS.md | 4 +- DESIGN.md | 2 +- README.md | 2 +- justfile | 4 + src/oauth/mod.rs | 2 +- tools/check-doc-references.py | 185 ++++++++++++++++++++++++++++++++++ 6 files changed, 194 insertions(+), 5 deletions(-) create mode 100644 tools/check-doc-references.py diff --git a/AGENTS.md b/AGENTS.md index d26bb77..1ba834f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -52,7 +52,7 @@ just serve Before review or handoff, run at least `just fix` and all relevant tests. Use `just test-all` for substantial changes. -In a fresh checkout or jj workspace, run `just check` once before `just fix` so the first compilation catches toolchain and dependency issues before formatting. +In a fresh checkout or jj workspace, run `just check` once before `just fix` so the workspace dependencies and generated build outputs are available. The frontend no longer generates or imports a `src/env.rs` module; browser configuration must not receive server-only `POLYMODEL_*` secrets. Server runtime configuration is loaded by the server modules from the process environment. ## Stack notes @@ -81,7 +81,7 @@ The 3D renderer (`three-d` + mesh parsing) runs in a **Web Worker** with its own - Use flex column for simple vertical page shells. Reserve CSS grid for actual two-dimensional layouts; grid containers combined with `min-height: 100vh` can create surprising stretched rows and large visual gaps. - During visual/layout debugging, use screenshots plus accessibility snapshots with boxes and computed-style inspection. Screenshots show symptoms; boxes/computed styles identify the CSS rule causing the layout. - The app renders every route inside a single `#[layout(AppShell)]` shell (`src/shell.rs`) with an `Outlet`; the shell owns the global header (wordmark, Browse, global search → `/search`, Publish CTA, session/account control) and the viewport height (`min-height: 100dvh`, flex column), so pages are `flex: 1` and never set `min-height: 100vh`. Product nav deliberately excludes `/foundation` and `/viewer` (reachable by direct URL only). Sign-in `return_to` is the current route, not a hardcoded path. -- `assets/styling/` follows a layered cascade, declared in that order in `src/main.rs`: tokens (`theme.css`) → reset/base (`base.css`) → reusable primitives (`primitives.css`) → shell chrome (`shell.css`) → per-route page styles (`home.css`, `thing-detail.css`, `profile.css`, `viewer.css`, `placeholders.css`). Keep `theme.css` and `DESIGN.md` in sync (the design detector reads `DESIGN.md`). +- `assets/styling/` follows a layered cascade, declared in that order in `src/main.rs`: tokens (`theme.css`) → reset/base (`base.css`) → reusable primitives (`primitives.css`) → shell chrome (`shell.css`) → per-route page styles (`home.css`, `thing-detail.css`, `profile.css`, `viewer.css`, `publish.css`, `placeholders.css`, `about.css`). Keep `theme.css` and `DESIGN.md` in sync (the design detector reads `DESIGN.md`). ### Lexicons & codegen diff --git a/DESIGN.md b/DESIGN.md index f75695d..b587338 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -190,7 +190,7 @@ The drafting-grid media well (`.blueprint-media`) and the shimmer skeletons (`.s ## 6. Do's and Don'ts ### Do: -- **Do** reuse the tokens in `assets/styling/theme.css` and the component vocabulary in `base.css`/`cards.css`; extend, don't fork. +- **Do** reuse the tokens in `assets/styling/theme.css` and the component vocabulary in `base.css`/`primitives.css`; extend, don't fork. - **Do** set all technical metadata (dimensions, formats, DIDs/handles, counts, status) in Ioskeley Mono. - **Do** keep body/placeholder contrast ≥ 4.5:1 in both schemes; move text toward Ink, not Muted, when close. - **Do** ship every interactive state — default, hover, focus, active, disabled, loading, error — with visible keyboard focus. diff --git a/README.md b/README.md index fd09c90..95fcac3 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ It has basic search and simple aggregated global and author feeds. ## Architecture and Evolution -The current design is a monolithic Dioxus fullstck webapp+appview server, acting as an OAuth (potentially confidential) client. It backfills and tails the firehose via [hydrant](https://hydrant.klbr.net/) into its Fjall data store. That event stream is teed and written to an SQLite database for additional indexing, which is currently used as the primary read target and to support immediate readback of user writes. This is very likely to change, perhaps by sharding the SQLite database by DID and/or moving more into the Fjall store. +The current design is a monolithic Dioxus full-stack webapp and appview server, acting as an OAuth (potentially confidential) client. [Hydrant](https://hydrant.klbr.net/) backfills and tails the firehose, retaining the raw event stream in its Fjall store. The event stream is also projected into SQLite; that SQLite appview projection is the primary read target and supports immediate readback of user writes. For a local write awaiting firehose confirmation, appview reads consult the `pending_writes` and `pending_deletes` tables so the local projection can win over a lagging PDS; Hydrant clears the matching pending operation when the commit arrives. This is very likely to change, perhaps by sharding the SQLite database by DID and/or moving more into the Fjall store. It also intends to support the draft spec for atproto permissioned data, for features such as drafts, private sharing, private lists, and so on. The core primitives will land upstream in Jacquard, and the concrete implementation will be a separate service binary from the main app server, ideally somewhat agnostic to the specifics of the app, acting as space host and permission broker. diff --git a/justfile b/justfile index 4577957..97a1daf 100644 --- a/justfile +++ b/justfile @@ -125,6 +125,10 @@ validate-gc-state *ARGS: validate-gc-jj-state *ARGS: python3 tools/validate-gc-jj-state.py {{ ARGS }} +# Check current-state documentation paths and architecture/style map references. +check-doc-references *ARGS: + python3 tools/check-doc-references.py {{ ARGS }} + # Create a sibling jj workspace for a Jira ticket or feature slug, then start a fresh change. workspace-create name: root="$(jj workspace root)" && workspace_path="$(dirname "$root")/{{ name }}" && jj workspace add "$workspace_path" -r @ diff --git a/src/oauth/mod.rs b/src/oauth/mod.rs index 588b5ef..88200d1 100644 --- a/src/oauth/mod.rs +++ b/src/oauth/mod.rs @@ -2,7 +2,7 @@ //! //! [`SqliteAuthStore`] implements [`ClientAuthStore`] and //! [`SessionSelector`] over the `oauth_sessions` and `oauth_auth_requests` -//! tables (see `migrations/001_projection_schema.sql`). Each row stores a +//! tables (see `migrations/0001_initial_schema.sql`). Each row stores a //! structured, queryable projection of the Jacquard session / auth-request types //! alongside a canonical JSON `payload`. Structured columns power key //! enumeration, by-DID/any selection, and deletes without the payload; only diff --git a/tools/check-doc-references.py b/tools/check-doc-references.py new file mode 100644 index 0000000..71e03ea --- /dev/null +++ b/tools/check-doc-references.py @@ -0,0 +1,185 @@ +#!/usr/bin/env python3 +"""Check current-state documentation references against source and the filesystem.""" +from __future__ import annotations + +import argparse +import re +import sys +import tempfile +from pathlib import Path + +DOCS = ("README.md", "DESIGN.md", "AGENTS.md", "src/oauth/mod.rs") +STALE_REFERENCES = ("cards.css", "001_projection_schema.sql") +REQUIRED_ARCHITECTURE_TERMS = ( + "SQLite", + "appview projection", + "Hydrant", + "Fjall", + "pending_writes", + "pending_deletes", +) +STYLE_DECLARATION = re.compile(r'asset!\("/assets/styling/([^"/]+\.css)"\)') +REQUIRED_SOURCE_FILES = ("src/main.rs", "migrations/0001_initial_schema.sql") + + +def fail(message: str) -> None: + raise ValueError(message) + + +def selected_documents(root: Path) -> list[tuple[Path, str]]: + documents: list[tuple[Path, str]] = [] + for relative in DOCS: + path = root / relative + if not path.is_file(): + fail(f"missing documentation source: {relative}") + documents.append((path, path.read_text(encoding="utf-8"))) + return documents + + +def check_paths(root: Path, documents: list[tuple[Path, str]]) -> None: + source = root / "src/main.rs" + if not source.is_file(): + fail("missing source of truth: src/main.rs") + declarations = STYLE_DECLARATION.findall(source.read_text(encoding="utf-8")) + if not declarations: + fail("src/main.rs has no stylesheet declarations") + for stylesheet in declarations: + if not (root / "assets/styling" / stylesheet).is_file(): + fail(f"stylesheet declared by src/main.rs does not exist: {stylesheet}") + for relative in REQUIRED_SOURCE_FILES: + if not (root / relative).is_file(): + fail(f"required current path does not exist: {relative}") + for path, text in documents: + if "assets/styling/" in text: + for reference in re.findall(r"assets/styling/([A-Za-z0-9_.-]+\.css)", text): + if not (root / "assets/styling" / reference).is_file(): + fail(f"{path.relative_to(root)} references missing stylesheet: {reference}") + for reference in re.findall(r"migrations/[A-Za-z0-9_.-]+\.sql", text): + if not (root / reference).is_file(): + fail(f"{path.relative_to(root)} references missing migration: {reference}") + + +def check_current_maps(root: Path, documents: list[tuple[Path, str]]) -> None: + text_by_name = {path.name: text for path, text in documents} + architecture_text = "\n".join(text_by_name[name] for name in ("README.md", "AGENTS.md")) + for term in REQUIRED_ARCHITECTURE_TERMS: + if term not in architecture_text: + fail(f"current architecture maps omit required term: {term}") + source = (root / "src/main.rs").read_text(encoding="utf-8") + expected_cascade = STYLE_DECLARATION.findall(source) + agents = text_by_name["AGENTS.md"] + cascade_line = next((line for line in agents.splitlines() if "layered cascade" in line), "") + if not cascade_line: + fail("AGENTS.md does not describe the stylesheet cascade") + positions = [cascade_line.find(stylesheet) for stylesheet in expected_cascade] + if any(position < 0 for position in positions) or positions != sorted(positions): + fail("AGENTS.md does not describe the stylesheet cascade in src/main.rs order") + + +def check_stale_references(documents: list[tuple[Path, str]]) -> None: + for path, text in documents: + for stale in STALE_REFERENCES: + if stale in text: + fail(f"{path.relative_to(path.parents[1]) if path.name == 'mod.rs' else path.name} contains stale reference: {stale}") + + +def validate(root: Path) -> None: + documents = selected_documents(root) + check_paths(root, documents) + check_current_maps(root, documents) + check_stale_references(documents) + + +def write_fixture(root: Path) -> None: + (root / "src/oauth").mkdir(parents=True, exist_ok=True) + (root / "assets/styling").mkdir(parents=True, exist_ok=True) + (root / "migrations").mkdir(exist_ok=True) + (root / "src/main.rs").write_text( + "\n".join(f'const STYLE: Asset = asset!("/assets/styling/{name}");' for name in ( + "theme.css", "base.css", "primitives.css", "shell.css", "home.css", + "thing-detail.css", "profile.css", "viewer.css", "publish.css", + "placeholders.css", "about.css", + )), + encoding="utf-8", + ) + for name in ( + "theme.css", "base.css", "primitives.css", "shell.css", "home.css", + "thing-detail.css", "profile.css", "viewer.css", "publish.css", + "placeholders.css", "about.css", + ): + (root / "assets/styling" / name).write_text("", encoding="utf-8") + (root / "migrations/0001_initial_schema.sql").write_text("", encoding="utf-8") + (root / "README.md").write_text( + "SQLite appview projection; Hydrant retains raw events in Fjall. " + "pending_writes and pending_deletes define the pending-operation boundary.\n", + encoding="utf-8", + ) + (root / "DESIGN.md").write_text("Use assets/styling/theme.css and primitives.css.\n", encoding="utf-8") + (root / "AGENTS.md").write_text( + "SQLite appview projection; Hydrant and Fjall; pending_writes and pending_deletes.\n" + "assets/styling/ follows a layered cascade: " + "theme.css base.css primitives.css shell.css home.css thing-detail.css profile.css " + "viewer.css publish.css placeholders.css about.css\n", + encoding="utf-8", + ) + (root / "src/oauth/mod.rs").write_text( + "See migrations/0001_initial_schema.sql.\n", encoding="utf-8" + ) + + +def self_test() -> None: + with tempfile.TemporaryDirectory() as directory: + root = Path(directory) + write_fixture(root) + validate(root) + (root / "assets/styling/cards.css").write_text("", encoding="utf-8") + (root / "DESIGN.md").write_text( + "Use assets/styling/cards.css.\n", encoding="utf-8" + ) + try: + validate(root) + except ValueError: + pass + else: + fail("negative stale-stylesheet fixture unexpectedly passed") + write_fixture(root) + (root / "migrations/001_projection_schema.sql").write_text("", encoding="utf-8") + (root / "src/oauth/mod.rs").write_text( + "See migrations/001_projection_schema.sql.\n", encoding="utf-8" + ) + try: + validate(root) + except ValueError: + pass + else: + fail("negative stale-migration fixture unexpectedly passed") + write_fixture(root) + (root / "assets/styling/about.css").unlink() + try: + validate(root) + except ValueError: + pass + else: + fail("negative missing-stylesheet fixture unexpectedly passed") + print("check-doc-references self-test passed") + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--self-test", action="store_true") + parser.add_argument("root", nargs="?", type=Path, default=Path(__file__).resolve().parents[1]) + args = parser.parse_args() + try: + if args.self_test: + self_test() + else: + validate(args.root.resolve()) + print(f"check-doc-references passed: {args.root.resolve()}") + except (OSError, ValueError) as exc: + print(f"check-doc-references failed: {exc}", file=sys.stderr) + return 1 + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) -- 2.51.2