Skip to content

Stage 0: CI & tooling foundation (fmt/clippy/test/wasm gate) - #1

Merged
EYH0602 merged 5 commits into
mainfrom
feat/stage-0-cli
Jul 8, 2026
Merged

Stage 0: CI & tooling foundation (fmt/clippy/test/wasm gate)#1
EYH0602 merged 5 commits into
mainfrom
feat/stage-0-cli

Conversation

@EYH0602

@EYH0602 EYH0602 commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

Summary

Implements plans/stage-0-ci-tooling.md.

Adds the reproducible CI gate for the workspace before any product code lands: a
GitHub Actions workflow (fmt, clippy, test, wasm-build) on a pinned,
SHA-locked toolchain and action set, plus supporting config. No product logic;
this stage installs the quality gate every later stage PR runs through.

Plan Steps

  • Pin exact toolchain 1.94.1 + rustfmt config (b4e8f68)
  • Drop unverified MSRV (rust-version) from workspace + all 4 crates (b4e8f68)
  • Bump 0.0.0 → 0.0.1 and regenerate Cargo.lock so --locked stays in sync (b4e8f68)
  • CI workflow: on/concurrency/permissions, ubuntu-24.04, --locked builds, SHA-pinned actions (94c70b9)
  • wasm job guards both ara-core and ara-wasm (94c70b9)
  • Dependabot for cargo + github-actions, weekly (94c70b9)
  • CONTRIBUTING.md documenting the CI-mirroring local checks (5f3ac00)
  • TODOS.md for deferred work — MSRV job, wasm-target clippy, docs/ (e792e42)

Deferred to a follow-up (documented, not silently dropped):

  • Branch-protection ruleset on main requiring the 4 checks (plan step 7 / T8).
    This is a repo setting, and GitHub can only select the check names after CI
    runs once — so it's a post-merge step, done once this PR's CI is green.

Review Status

Eng Review: CLEAR (/plan-eng-review, 8 findings folded, 0 critical gaps).
Outside voice (Codex) ran — surfaced the floating toolchain pin, lockfile/version
sequencing, unenforced gate, and ara-wasm coverage; all folded. See the
## GSTACK REVIEW REPORT in the plan file. Implementation independently
spec-reviewed (all 8 items PASS).

Test Plan

All four CI commands were run locally and pass (they mirror the workflow exactly):

  • cargo fmt --all --check
  • cargo clippy --workspace --all-targets --locked -- -D warnings
  • cargo test --workspace --locked
  • cargo build -p ara-core -p ara-wasm --target wasm32-unknown-unknown --locked
  • Confirm all 4 CI jobs go green on this PR, then enable the main branch-protection ruleset (T8).

EYH0602 added 5 commits July 8, 2026 10:51
Add stage-overview plus per-stage plans (CI, core parse/validate, DAG
layout, wasm viewer, serve+live-reload, hub deploy). First published
release 0.1.0 is cut after Stage 4; Stage 5 ships as 0.1.1.
Incorporate accepted eng-review decisions into the Stage 0 CI plan:
exact 1.94.1 toolchain pin, --locked builds + lockfile regeneration,
drop unverified MSRV (root + per-crate), workflow on/concurrency/permissions,
drop inert [workspace.lints], wasm-build ara-wasm too, SHA-pinned actions +
ubuntu-24.04 + scoped Dependabot, and a branch-protection gate. Adds NOT-in-scope,
failure-mode, parallelization, and implementation-task sections plus the review
report. Track deferred work (T-MSRV, T-WASM-CLIPPY, T-DOCS) in TODOS.md.
Plan steps 1-3,5: exact toolchain pin (rust-toolchain.toml) + rustfmt
config; remove rust-version from [workspace.package] and all 4 crate
manifests; bump workspace 0.0.0 -> 0.0.1 and regenerate Cargo.lock so
--locked CI stays in sync. (T2, T5)
Plan steps 4,6: fmt/clippy/test/wasm jobs on ubuntu-24.04 with --locked
builds and SHA-pinned actions (checkout v7.0.0, rust-cache v2.9.1);
on/concurrency/permissions top-matter; wasm job guards ara-core AND
ara-wasm. Dependabot watches cargo + github-actions weekly. (T1,T3,T6,T7)
Plan step 8: document the CI-mirroring commands (fmt/clippy/test/wasm,
all --locked) so contributors get a green PR on the first try.

@fenfenai fenfenai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

Reviewed 20 files (+844/-10) across bug detection, guidelines compliance, and test/CI-correctness aspects. This is a config-only stage — no product logic — and it is well-executed: exact toolchain pin (1.94.1), SHA-pinned actions, --locked on every building job, least-privilege permissions: contents: read, and rustfmt using only stable options (max_width, edition) so it won't break on the stable channel. The Cargo.lock regen matches the 0.0.0 → 0.0.1 bump, so --locked stays green. The trivial dependency-free skeleton crates guarantee the fmt/clippy/test/wasm commands pass.

No findings at confidence ≥75 that block merge. Approving.

Minor (non-blocking)

Job name is wasm, but the CHANGELOG and PR body call it wasm-build (confidence: 80). The actual CI job id/name is wasm (.github/workflows/ci.yml:44-45), while CHANGELOG.md and the PR description list the gate as wasm-build. This is cosmetic today, but the deferred branch-protection step (plan step 7 / T8) selects required status checks by their exact job name — configuring protection off the docs would look for a non-existent wasm-build check. Suggest aligning the CHANGELOG/PR wording to wasm (or renaming the job) before T8 is set up.

Positive Observations

  • Determinism rationale is captured inline (exact patch pin comment, --locked intent) — the why survives.
  • Deferred work (MSRV job, wasm-target clippy, docs/ lifecycle, branch protection) is documented in TODOS.md rather than silently dropped.
  • Dependabot correctly covers both cargo and github-actions, so the SHA pins won't rot silently.

Comment thread CHANGELOG.md
the defensive `bara-*` placeholders (`bara-core`, `bara-cli`, `bara-wasm`,
`bara-viewer`).
- Root README documenting the workspace layout and install path.
- CI workflow (`fmt`, `clippy`, `test`, `wasm-build`) on GitHub Actions with a

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CI job is named wasm (.github/workflows/ci.yml:44-45), not wasm-build. Minor, but the deferred branch-protection ruleset (plan T8) requires status checks by exact name, so align this to wasm to avoid selecting a non-existent check later. (confidence: 80)

@EYH0602
EYH0602 merged commit d0e8669 into main Jul 8, 2026
4 checks passed
@EYH0602
EYH0602 deleted the feat/stage-0-cli branch July 8, 2026 21:40
EYH0602 added a commit that referenced this pull request Jul 12, 2026
All 6 plans in plans/ were for features that have shipped, so per CLAUDE.md's
plan→docs lifecycle they move to docs/ as design records and out of plans/.

- New design docs: stage-0-ci-tooling, stage-2-dag-layout, real-corpus-no-panic
- Stage 0 (#1), Stage 2 (#5), issue #3 (#4) had no docs/ counterpart → written
- issue #9 layout modes already folded into docs/stage-3-viewer.md → just removed
- ara-runtime-impl-plan + stage-overview were meta-planning artifacts fully
  realized in code + per-stage docs → removed
- Retire completed TODOs T-DOCS (this migration) and T-VIEWER-DIST-PACKAGING
  (resolved by the Stage 4 include_dir! embed), and the obsolete
  T-STAGE4-VERSION-BUMP (Stage 3/4 both shipped)
- Fix dangling plan references in README.md and TODOS.md T-MSRV
EYH0602 added a commit that referenced this pull request Jul 12, 2026
)

* docs(plans): retire shipped issue-7 plan, refresh Stage 5 hub-deploy plan

Remove plans/issue-7-tree-list-mode.md — shipped in #14 (0.1.2) and folded
into docs/stage-3-viewer.md + docs/manifest-schema.md.

Refresh Stage 5 plan against the shipped Stage 4 code: correct the version
target (0.1.2 -> 0.1.3), resolve hub routing (path-based /a/{id}/ with
document-base-relative viewer URLs) and Docker assets (embedded-only), drop
the now-moot precompressed-assets step, and add a --host flag + broken-ARA
skip-on-ingest.

* docs(plans): fold Stage 5 eng review findings into hub-deploy plan

Eng review (14 findings + outside voice) folded into the Stage 5 plan:
Docker needs no wasm toolchain (bundle committed + baked via include_dir!),
harden base-href splice + id charset, no SPA fallback at hub root, share
serve_cached_manifest, define empty/bad-root startup, clap ArgGroup for mode
select, 308 (not cacheable 301) for known ids only, drop unused parsed
Manifest on the hub path, wire --assets end-to-end, add wasm + headless
browser tests for D1. Defer per-ARA figures (ServeDir cannot nest under a
{id} param) and record the static-export alternative (D3: keep server).

Add T-HUB-FIGURES and T-STATIC-EXPORT to TODOS.md.

* feat(viewer): resolve API/live URLs relative to document base

Plan step 1/4: Viewer relative API/WS URLs (T5)

Make ManifestSource default URLs relative (api/manifest, api/live) so they
resolve against document.baseURI — local serve unchanged (page at /), hub
serves under <base href="/a/{id}/">. Refactor the ws:// scheme-swap core into
a pure, wasm-testable ws_url_from_base(base, path). Native test guards the
relative defaults; wasm test proves resolution for root and /a/{id}/ bases.

Note: viewer source changed -> embed regen follows in step 4.

* feat(serve): hub ingest cache — parse-once, immutable, id-guarded

Plan step 2/4: Hub cache ingest (T2, T8, T12)

Add serve/hub.rs: ingest(root) scans immediate child dirs into an immutable
Arc<HashMap<id, Arc<CachedAra>>> (lock-free reads, no watcher). Ids constrained
to [A-Za-z0-9._-]+ (rejects spaces/non-ASCII/slash/..) — one guard covering
path-segment, URL, and HTML-escape safety. Broken ARAs and duplicate ids are
logged/skipped, never fatal; an unreadable root is fatal (Err). Per-entry read
errors are skipped, not dropped.

cache.rs: manifest field is now Option<Arc<Manifest>>; from_dir_lean() drops
the parsed graph the hub never reads (~2x resident memory saved per ARA) while
from_dir() keeps it for local serve. Shared build() produces identical json +
etag + figures_dir either way.

Native tests cover: two good, one broken skipped, empty->empty, missing->Err,
rejected charset, duplicate id kept-first, lean drops manifest.

* feat(serve): hub router + CLI mode selection

Plan step 3/4: Hub routing + CLI args (T3, T4, T7, T9, T10, T11, T13)

- clap ArgGroup models local (positional dir) vs hub (--hub --ara-root) so bad
  combos fail at parse time; add --host (default 127.0.0.1, honors 0.0.0.0 for
  containers) (T9).
- Extract serve_cached_manifest(&CachedAra, &HeaderMap) shared by the local and
  per-ARA hub manifest handlers — one conditional-GET source of truth (T7).
- build_hub_router: /a/{id}/api/manifest (404 if unknown), /a/{id}/ index with
  <base href> injected + no-cache, / index listing, strict shared-asset
  fallback. No /api/live, no watcher.
- Base-href splice guarded: no <head> -> 500, never a silent base-less page (T3).
- Root asset serves real files only, else 404 — no SPA fallback (T4).
- Bare /a/{id}: known -> 308, unknown -> 404 (never 308-to-404) (T10).
- --assets wired end-to-end in hub (on-disk index + shared ServeDir) (T13).
- hub oneshot tests incl. two sequential reads = same etag (pure cache hit, T11)
  + full parse-arg matrix; also applies pending rustfmt to cache.rs/hub.rs.

run() branches to hub when --hub is set; local path behavior unchanged.

* build: Docker image, deploy docs, CI smoke test, embed regen, 0.1.3

Plan step 4/4: Regen embed + Dockerfile + CI + docs (T1, T6, T14)

- Regenerate the embedded viewer bundle (step 1 changed viewer source, so
  viewer-embed-fresh required a regen).
- Dockerfile: multi-stage musl -> distroless, cargo-chef dep cache, NO wasm
  toolchain (viewer baked in via include_dir!). Pins the repo toolchain before
  adding the musl target and points CC/linker at musl-gcc. Verified: image
  builds, 3.1 MB compressed (< 20 MB budget) (T1).
- .dockerignore keeps the context small (target/, corpus, git, plans/docs).
- docs/deploy.md: hub routing, ingest, Docker/compose, systemd, Caddy/nginx
  reverse-proxy compression, and the manifest.json-fallback-inert-on-hub note
  (T14). Folds the plan into docs per CLAUDE.md.
- CI: docker job (build + image-size budget + container smoke test:
  manifest 200/etag/304, /a/{id}/ html+base, wasm application/wasm).
- viewer wasm test: relative api/manifest resolves under <base href="/a/x/">
  in a real browser (T6).
- Bump workspace 0.1.2 -> 0.1.3 + ara-core pins; CHANGELOG [Unreleased].

* docs: retire stage-5 plan (folded into docs/deploy.md)

Plan fully implemented across the 4 preceding commits; per CLAUDE.md the plan is
rewritten as a design doc (docs/deploy.md) and removed from plans/.

* fix(docker): link the musl binary statically for distroless

The CI docker job failed with 'exec /ara: no such file or directory'. The
CARGO_TARGET_..._LINKER=musl-gcc override made musl-gcc the link driver, which
links dynamically against /lib/ld-musl-x86_64.so.1 — absent from
distroless/static, so the loader-less image can't exec the binary.

Drop the linker override (rustc's musl target links fully static via its
self-contained startup objects) and pin RUSTFLAGS=-C target-feature=+crt-static.
Keep only the CC=musl-gcc override, which is what actually fixed the build
script's -m64 error. Verified: the extracted binary is static-pie with no
interpreter, and the container passes the full smoke test locally.

* style(design): FINDING-001/004 — fix WCAG AA contrast for muted + accent text

- --muted: #90856f → #726751 (3.38:1 → 5.16:1 on --bg, 3.10:1 → 4.75:1 on --panel2)
- Add --accent-text: #8c4414 for text-on-light uses (3.67:1 → 6.60:1 on --bg)
- Original --accent preserved for borders/backgrounds where 3:1 suffices

* style(design): FINDING-002 — add focus-visible ring to tree-list rows

Tree rows are role="button" + tabindex="0" but had no visible keyboard focus
indicator. Adds a 2px accent outline matching the graph-mode node focus style.

* style(design): FINDING-003 — respect prefers-reduced-motion

Disable skeleton shimmer animation when the user's OS has motion reduction
enabled. The only animation in the viewer is this loading shimmer.

* style(design): FINDING-005/006 — enlarge touch targets, bump body font

- Controls: height 1.75rem → min-height 2.25rem (28px → 34-36px)
- Body font-size: 14px → 15px (closer to the 16px accessibility guideline
  while preserving data-dense tool proportions)
- Applies to .toolbar-search/.toolbar-select, .layout-toggle, and .btn

* style(design): FINDING-007 — use color-mix for --warn strikethrough

Replace raw rgba(162, 59, 45, 0.4) with color-mix(in srgb, var(--warn) 40%,
transparent) so the decoration color tracks the token if --warn changes.
Matches the same technique used in the status pills.

* build: regenerate embedded viewer bundle for design fixes

The design-review CSS fixes (FINDING-001..007) changed the ara-viewer source,
so `ara serve` shipped stale UI when run without --assets. Regenerated via
scripts/embed-viewer.sh so the embedded bundle matches the source and CI's
viewer-embed-fresh check passes.

* docs: fold shipped plans into docs/, remove plans/

All 6 plans in plans/ were for features that have shipped, so per CLAUDE.md's
plan→docs lifecycle they move to docs/ as design records and out of plans/.

- New design docs: stage-0-ci-tooling, stage-2-dag-layout, real-corpus-no-panic
- Stage 0 (#1), Stage 2 (#5), issue #3 (#4) had no docs/ counterpart → written
- issue #9 layout modes already folded into docs/stage-3-viewer.md → just removed
- ara-runtime-impl-plan + stage-overview were meta-planning artifacts fully
  realized in code + per-stage docs → removed
- Retire completed TODOs T-DOCS (this migration) and T-VIEWER-DIST-PACKAGING
  (resolved by the Stage 4 include_dir! embed), and the obsolete
  T-STAGE4-VERSION-BUMP (Stage 3/4 both shipped)
- Fix dangling plan references in README.md and TODOS.md T-MSRV

* docs: drop dangling plan references in README and TODOS

Point README at docs/ instead of the removed master plan, refresh T-MSRV
context to the crates.io publish, and retire completed/obsolete TODOs
(T-DOCS, T-VIEWER-DIST-PACKAGING, T-STAGE4-VERSION-BUMP).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants