Verification - HULKs/labello GitHub Wiki

Verification and acceptance

This contract separates implementation evidence, mechanically enforced checks, and independent acceptance. Passing commands is necessary evidence; it does not replace review of the intended behavior, production path, or risks.

Canonical entry point

From the repository root, verify a branch and any local changes against its comparison base with:

./scripts/verify.sh changed origin/main

CI uses the equivalent ./scripts/verify.sh ci <pull-request-base-sha> command contract. The hosted workflow first calls its audit and plan stages, then runs the selected verification stages as separate parallel jobs. Calling ci without a stage runs those same checks sequentially for local reproduction. Both forms classify every changed path, and an unclassified path fails closed. Documentation-only changes run the documentation profile; every other change runs the baseline, and browser-affecting changes also run the release Trunk build. Use ./scripts/verify.sh all to run the baseline and browser build without changed-path optimization, or ./scripts/verify.sh classify <base> to inspect the selected profiles.

Hosted native-build jobs refresh Ubuntu's ubuntu.sources package index only. The required native libraries come from Ubuntu; third-party repositories bundled with the runner image must not block their installation. APT signature and hash verification remain enabled.

The required baseline is:

cargo fmt --all -- --check
cargo fmt --manifest-path apps/egui-mcp-inspector/Cargo.toml --all -- --check
cargo clippy --locked --workspace --all-targets --all-features -- -D warnings
cargo clippy --locked --manifest-path apps/egui-mcp-inspector/Cargo.toml --all-targets --all-features -- -D warnings
cargo test --locked --workspace --all-features --exclude labello-ui
cargo test --locked -p labello-ui --all-features
cargo check --locked --manifest-path apps/egui-mcp-inspector/Cargo.toml
cargo test --locked --manifest-path apps/egui-mcp-inspector/Cargo.toml --all-features
cargo check --locked -p labello-wasm --target wasm32-unknown-unknown

The workspace and labello-ui tests are disjoint so CI can run them in parallel. The dedicated UI command uses all features, which keeps inspector-presets coverage explicit without executing those tests twice. Browser, shared-rendering, browser-persistence, browser-import, deployment-asset, and relevant shared dependency changes additionally run, from apps/labello-wasm:

trunk build --release --locked

CI preserves those test selections with pinned cargo-nextest 0.9.143 and a separate doctest job:

cargo nextest run --locked --workspace --all-features --exclude labello-ui
cargo nextest run --locked -p labello-ui --all-features
cargo test --locked --workspace --all-features --doc

The final Cargo command is intentionally retained because Nextest does not run doctests on stable Rust. The local changed, baseline, and all commands use ordinary cargo test, so contributors do not need cargo-nextest. The ci command requires it and fails if the pinned prebuilt tool was not installed.

Every Cargo command that resolves dependencies and the Trunk build use locked mode. A stale tracked lockfile is therefore a failure and is never implicitly rewritten by verification.

Prerequisites are Python 3.10 or newer for documentation checks, Rust 1.98.0, Cargo, rustfmt, Clippy, the wasm32-unknown-unknown target, Trunk 0.21.14, and the native libraries required by eframe. CI declares the applicable prerequisites per job in .github/workflows/ci.yml, restores job-scoped dependency build artifacts for the root or standalone-inspector target directory, downloads pinned prebuilt Trunk and cargo-nextest binaries only where needed, and invokes this script rather than duplicating verification commands. Job identity is part of each cache key, preventing parallel jobs from racing to publish one immutable cache; keys also include the Rust toolchain, Cargo manifests and lockfiles, and relevant compiler environment. Caches therefore work across compatible hosted runners without relying on machine reuse. The final Testing job fails closed unless the plan and every selected parallel job succeeded, while requiring an unselected conditional job to be skipped. The script's audit checks those links, the PR evidence sections, shell syntax, diff whitespace, and complete classification of tracked paths.

Rust compatibility contract

rust-toolchain.toml is the compiler source for local commands and CI bootstrap. The root workspace and standalone inspector workspace both require Rust 1.98. Every maintained package inherits that MSRV. Commands from either directory resolve to the exact 1.98.0 compiler, including rustfmt, Clippy, and the WASM target. Install the pinned prerequisites with:

rustup show

Rustup reads the repository toolchain file and installs the declared compiler, components, and target. Cargo manifests declare the MSRV for both workspaces. CI uses that toolchain to build and test the supported matrix below. Release images include the same compiler and build prerequisites. Keep these settings aligned when changing the baseline; there is no custom toolchain-policy audit.

Workspace or target Required locked compatibility evidence
Root, native host All maintained members including server and deployment tool; all targets/features in Clippy and all features in tests, with separate UI and doctest coverage
Standalone inspector, native host Its own lockfile; formatting, all-target/all-feature Clippy, compiler check, and all-feature tests
Browser, wasm32-unknown-unknown Production WASM app compiler check and release Trunk build with the root lockfile
Release, Linux x86-64 Debian Bookworm Same baseline and browser build, then locked release server/deployment-tool builds inside the configured immutable image

The inspector is a native application. Browser-only code is verified through labello-wasm; inspector-only features do not imply browser support. Native GUI and Chromium evidence retain the limits and risk-specific requirements below.

Dependency updates must pass this matrix with both tracked lockfiles on 1.98.0. Do not repair lockfiles during verification. An MSRV increase requires an explicit coordinated change to workspace policies, the toolchain pin, CI/release configuration and images, and current documentation. Optional checks on newer compilers do not replace this baseline. The external release image must be verified before rollout as described in deployment.

Risk profiles

The script selects profiles conservatively from changed paths. The machine baseline is shared; the following checks are additional acceptance evidence. If a change crosses profiles, apply all of them.

UI and shared rendering

Read ui-design-guidelines.md and ui-ownership.md. Add the smallest egui_kittest regression covering behavior, geometry, and AccessKit semantics. Exercise long content and loading, empty, stale, failure, disabled, keyboard, focus, and overlay states that the change can affect. Inspect applicable shared states at the viewport, DPR, zoom, larger-text, and keyboard matrix in the UI guidelines. Native inspector evidence proves shared egui behavior only; use Chromium for browser layout, zoom, input, accessibility-tree, networking, cookie, or IndexedDB claims. Shared-rendering changes require the locked release Trunk build.

For completion input, build the server and release browser distribution and use the isolated Python environment from the browser input procedure. Run python apps/labello-wasm/tests/completion_input.py --kind bounding_box and repeat with --kind skeleton. Optional --width, --height, and --dpr select the browser matrix. This creates disposable synthetic work and checks that trusted repeated Space keydowns cannot complete successive images, while release and repress can. It reports only browser settings and aggregate counts; shared UI tests cover custom shortcuts, focused buttons, review, and migration.

For silent image navigation, run python apps/labello-wasm/tests/silent_loading.py --kind bounding_box and repeat with --kind skeleton, with and without --review. The procedure uses the same private Python environment and disposable production server as the completion check. It delays next/previous assignment requests and samples the retained image in memory to detect dimmed or blank frames. --width, --height and --dpr select the browser matrix; --zoom applies actual 200% Chrome tab zoom. Optional --artifacts captures only the application bars for review evidence. It does not prove migration-specific transactions, failure recovery or absence of every possible spinner; shared UI tests and native inspection cover those presentation states.

For Objects/Overview queues, run python apps/labello-wasm/tests/workflow_items.py --kind bounding_box and repeat with --kind skeleton. It uses disposable production data to check cross-image Objects work, C → B → A → B → C history, separate annotation Overview completion, focused review, review Overview submission through the shortcut and visible action, and one streak unit per completed item. Optional --artifacts captures only the workflow selector. Storage tests cover multi-user handoff, prelabel replacement, migration transactions, and exact score/replay invariants.

To measure navigation, build the release server and browser distribution, then add --server target/release/labello-server --latency-budget-ms 500 to each Objects/Overview command. This opt-in Chromium check clicks the visible Submit, Approve and Previous buttons in annotation and review. Timing starts at pointer release and ends after successful display validation, the new working-image draw, an enabled primary action and the next animation frame. It fails if any measured transition reaches the budget and checks that workflow requests start before their dispatch frame finishes. The probe uses the fixture's fixed 1440 × 1000 viewport at DPR 1 and 100% zoom; pixels remain in browser memory. It also checks item order and completion through the API. Terminal empty queues are checked functionally and have no next-item latency sample. These local synthetic measurements exclude startup and do not establish a production network or hardware latency guarantee. Record larger-dataset and migration measurements separately, including their readiness criteria. Include both fresh forward work and history return while prefetch runs; warm history alone misses queue contention. Repeat with --browser firefox when investigating Firefox or Zen. Install Playwright Firefox in the same private environment and run with a display, or the inspector guide's Xvfb setup and LIBGL_ALWAYS_SOFTWARE=1. Record first-use samples separately from warmed operation, and report budget failures rather than dropping them.

For working-preview decoder changes, run python apps/labello-wasm/tests/preview_decode.py --server target/release/labello-server in Chromium and repeat with --browser firefox under the same display setup. It loads the built production decoder module and checks asynchronous completion, RGBA output, malformed input, mismatched dimensions and bitmap cleanup. Client unit tests cover encoded-size, profile and header validation before allocation; the workflow matrix exercises decoding through the production WASM app.

For native inspection, follow the inspector guide. It includes headless startup, MCP readiness checks, independent parallel instances, and evidence tied to the tested checkout. Headless execution does not remove the visual checks or replace the Chromium evidence above.

Browser and WASM

Read ui-ownership.md, ui-design-guidelines.md, and the browser portions of operations.md. Run the WASM compiler check and locked release Trunk build. In Chromium, exercise startup plus the affected networking, credentials, persistence, folder import, responsive layout, input, and failure paths. Record browser version, viewport, DPR/zoom, accessibility inspection, and unsupported coverage. Do not infer browser behavior from the native inspector.

For startup performance, build the server and release browser distribution, then run cargo run --locked --release -p labello-deploy -- compress-browser apps/labello-wasm/dist. Use the isolated Python environment from the browser input procedure to run python apps/labello-wasm/tests/startup.py --browser chromium, then Firefox with --browser firefox --headed under the documented Xvfb setup when needed. The default benchmark uses five fresh contexts, 50 Mbps per static response and 40 ms latency per request in one reused browser process. It carries only a disposable session across contexts. The default --screen datasets measures the first draw of the fixture's recommended-dataset button and verifies that clicking it opens the dataset. The draw probe uses a fixed point inside that button at the benchmark viewport, confirmed by an in-memory screenshot. It fails when median navigation-to-dataset-list-draw time reaches 1000 ms. Use --screen restored-image to carry a workspace preference and measure the first draw of the restored synthetic image instead. Reports contain timings, aggregate sizes and bounded request categories only. Optional --mbps, --latency-ms, --runs, --dist and --budget-ms select a measurement profile. This local network model does not prove production TLS, throughput, API latency or user hardware; verify those after the authorized browser and Caddy rollout.

Pen-input changes also run the focused production-client checks in Stylus input. Record CDP-generated events separately from scripted DOM events. iPadOS Safari/Firefox support requires the named physical-device procedure; desktop WebKit is only adapter evidence.

Domain, events, and schema

Read architecture.md, persistence.md, and any affected import or UI ownership contract. Cover validation and invalid transitions, replay at every event boundary, current and supported legacy wire decoding, schema compatibility, digest or provenance behavior, and negative geometry/identifier bounds. A persisted-shape change also requires interrupted migration and historical replay evidence across every affected artifact.

Storage, migration, ingestion, and import

Read persistence.md, import.md, and operations.md. Test the complete lock/reload/validate/ append/replay/cache-invalidate transaction, atomic publication, no-replace and rollback behavior, cache rebuilding, restart and interruption recovery, limits, duplicate and invalid input, and concurrent or stale-assignment races relevant to the change. Import changes must cover parse, plan, build, verification, publication, durable job recovery, provenance, and bounded-resource failures.

API and security boundaries

Read api.md, configuration.md, and operations.md. Cover route and role matrices, exact assignment ownership, authentication, OAuth state and flow cookies, CSRF, credentialed CORS, request limits, untrusted-input conversion, safe public errors, and failure responses. Inspect logs for the redaction contract: no credentials, raw URLs or bodies, filenames or source paths, image content, annotation geometry, review comments, or idempotency values. Test denied and cross-dataset cases, not only the authorized path.

Documentation only

Run ./scripts/verify.sh docs. Review the changed content against current code and tests, check every changed local link and anchor, run git diff --check, and inspect the focused diff. Check affected behavior against its complete flow. Documentation-only changes do not require the Rust baseline unless they exercise generated contracts or examples. Run python3 scripts/docs.py check for local links, anchors, metadata, and wiki publication coverage.

Infrastructure changes never receive the documentation-only optimization. They run the baseline, and dependency/workflow/script changes conservatively run the browser build as well. An unknown path has no profile: if the classifier cannot prove a surface irrelevant, verification fails instead of skipping it.

Release workflow, deploy workflow, deployment/, and tools/ changes use the infrastructure profile. The baseline runs the Rust deployment transaction tests. The audit also rejects SSH, sudo, package installation, and Python in the deploy workflow and requires its pinned GitHub CLI checksum, local verify-release/receive/status boundary, immutable-release check, attestation signer, and flushed barrier documentation. Real guest systemd, mount, Caddy, TLS, and GitHub environment tests remain manual rollout evidence.

Workflow scope

The requested task determines which lifecycle stages the agent owns. These boundaries do not waive verification or independent acceptance:

Request Endpoint and owner
Analyze or review Findings and evidence; no implementation or publication unless requested
Clean up or implement a change, then open a PR Finish the authorized edits, verify, then publish a draft marked Awaiting CI
Package existing changes as a PR Verify and publish the prepared scope as a draft marked Awaiting CI; report implementation blockers to the caller
Implement an existing issue end to end Implement, verify, publish, fix CI failures, and complete the CI-gated Ready for review handoff
Ready a draft for review Manage CI state and handoff metadata; return implementation failures to the implementation owner
Independently verify Audit the requirements, final diff, and evidence; report an acceptance decision separately from implementation

The repository skills implement these stages: package current changes, open a draft, implement an issue, ready for review, and independent verification. Finish edits authorized by the user before entering the packaging stage. Publication alone does not authorize CI management, assignments, or project transitions. A later request can extend that scope without repeating completed preparation when the verified revision and base remain unchanged.

For a PR without an issue, use the user's requirements as its acceptance contract and apply issue/project updates only to existing linked items. Do not create an issue to satisfy the handoff checklist. Merging, closing issues, and marking work accepted require user authorization as well as the gates below.

Before coordinating multiple issues or testing dependent PRs together, follow parallel development for worktree ownership and verification of the combined group. Each PR still needs its own required CI.

Evidence and independent acceptance

The pull-request template is the required proof bundle. It records:

  • changed behavior or contract and the complete production ownership path;
  • every acceptance criterion mapped to concrete evidence;
  • regression protection and why it would fail before the fix;
  • exact commands, outcomes, environment, and material limitations;
  • required visual/browser states and artifacts;
  • normative documentation reviewed or updated;
  • unresolved risks and checks not performed; and
  • preservation of unrelated worktree changes.

Visual evidence in pull requests

Every pull request with any visual change must include screenshots or recordings in its Visual or browser evidence section when the draft is opened. This includes changes to layout, styling, text, icons, and visible interaction states, regardless of which files implement them. Apply this requirement when updating an existing draft too.

Capture the affected states from the verified implementation. Show before and after for existing views where a comparison is available; for a new view, show the resulting view. Explain any unavailable before capture. Use a recording when motion or an interaction sequence cannot be demonstrated by still images. Caption each artifact with the change it demonstrates, state or scenario, viewport, DPR/zoom or native scale, tested revision, and whether it came from the native inspector or Chromium. Record any uncommitted diff at capture time and its resulting published commit. Refresh evidence when later edits change the depicted result.

Embed the captures or link directly to hosted artifacts that PR reviewers can open. Local filesystem paths, inspection claims, test output, and accessibility trees alone do not satisfy this requirement. Read back the published PR and check that its evidence links resolve to the intended captures. If safe capture or reviewer-accessible hosting is unavailable, report the blocker and leave publication incomplete. Use Not applicable only when there is no visual change, with a brief explanation; browser checks may still apply.

Use the inspection procedures and coverage limits in the UI and browser risk profiles above. Prepare captures using disposable, non-sensitive content and apply the artifact redaction requirements below before uploading. Keep evidence outside tracked and runtime dataset paths.

Artifact safety and review gates

Artifacts and logs must follow operations.md: never upload secrets, credentials, runtime datasets, raw request data, image bytes, annotation geometry, review comments, uploaded filenames, or import paths.

After assembling the proof bundle and publishing a draft, the contributor leaves the change Awaiting CI. The required check must succeed for the pull request's exact current head SHA; a local run or success on an earlier head is not a substitute. A failed check returns the change to implementation, while a pending, missing, cancelled, or inaccessible check is recorded as not verified.

Within an authorized review handoff, only after exact-head CI success may the change become Ready for review. At that boundary, use the pull-request author as the accountable implementation owner and assign the pull request and any linked issue to that user. Keep the existing reviewer requests unchanged. Request review as a lifecycle transition by moving project items to In review and reporting the change as Ready for review. The agent does not add or remove requested reviewers.

An independent human or separately instructed verification agent then reconstructs the contract from the original issue, audits the final production diff and proof bundle, and tries to falsify each claim. High-risk review follows the complete applicable transaction, failure, recovery, authorization, compatibility, and redaction boundaries. Only after that review and all required checks pass may an issue be accepted or closed.

Repository enforcement

The CI pull-request workflow exposes descriptive parallel jobs behind the required Testing status check. Its aggregate job is the stable branch protection contract; the worker job names may evolve without changing that contract. The workflow has read-only repository permissions and uploads no artifacts. Repository administrators must configure a branch protection rule or ruleset for main that:

  • requires pull requests and at least one approving independent review;
  • requires Testing to pass on the current head;
  • blocks direct pushes and force pushes, including administrator bypass unless an audited emergency procedure explicitly applies; and
  • requires conversations to be resolved before merge.

The workflow file cannot activate repository-hosted branch protection by itself. The rule must be enabled in GitHub before this contract is considered fully enforced; record that repository-setting check in rollout evidence.

Export round-trip verification

Export changes require storage capture, source mutation, cancellation, publication failure, retention/restart, and archive integrity checks. API checks exercise unauthenticated, non-admin, CSRF, revoked-role, safe-error, HEAD, and streamed GET paths. UI checks additionally exercise real browser preflight, start, download, reload/history, cancellation and retry, including the documented viewport and keyboard matrix.

Generate both profiles through the production export service, extract them, and reimport through the production browser-folder protocol:

mkdir -p .worktrees/export-reader-artifacts
LABELLO_EXPORT_ROUND_TRIP_ARTIFACTS="$PWD/.worktrees/export-reader-artifacts" \
  cargo test --locked -p labello-storage export::round_trip

Use a new artifact directory for each run; the test refuses to overwrite profile directories. It checks hashes, distinct classes with equal names, object counts, normalized geometry at 1e-6, Visible/Occluded/Not present states, keypoint names, empty coverage and train/val/test preservation. It also asserts the documented loss of native identity/history and materialized pose boxes. These fixtures are synthetic and contain no user data.

The reader check uses CPython 3.12, a private virtual environment, pinned Ultralytics 8.4.125 and CPU Torch 2.8.0/Torchvision 0.23.0. Dependency setup requires network access; the actual reader script refuses socket connections, uses a local repository font, constructs no model, and checks that production artifacts remain unchanged. Install into the private environment only:

python3.12 -m venv .worktrees/export-reader-venv
.worktrees/export-reader-venv/bin/python -m pip install \
  --index-url https://download.pytorch.org/whl/cpu \
  torch==2.8.0+cpu torchvision==0.23.0+cpu
.worktrees/export-reader-venv/bin/python -m pip install \
  -r crates/labello-storage/tests/export-reader-requirements.txt
.worktrees/export-reader-venv/bin/python \
  crates/labello-storage/tests/export_reader.py \
  .worktrees/export-reader-artifacts \
  --report .worktrees/export-reader-report.json

The dataset reader must report both production profiles, preserved image and object counts, zero network attempts, zero constructed models, and unchanged input artifacts. Empty-only validation/test fixtures can produce the reader's expected training warnings. This command validates dataset reading, not training or prediction. Keep the dependency freeze, exact interpreter and reader versions, command logs, archive hashes, and JSON report with acceptance evidence. The reader check supplements canonical verification; it does not replace it.

Concurrent polling performance

The opt-in Linux test tests::polling_performance::nine_client_polling_performance uses the production Axum router with authenticated sessions, authorization, CSRF, real storage transactions, and disposable synthetic metadata. It does not contact production or use real user data. It creates 6,928 images with eight annotations and eight versions per annotation, then warms statistics and presence. Nine concurrent clients each perform five claim/submit/statistics/presence/availability cycles. This is an accelerated contention workload, not a simulation of human labeling speed. Image delivery, browser rendering, TLS and proxy latency are outside this measurement; route handlers and response bodies are exercised. This benchmark uses the image-assignment endpoint, not Objects/Overview item display. Storage workflow regressions separately bound image-state and event-log reads during warm item claims, display and submission in both annotation and review, including claims after a prefix of finished images, and check retained and unseen reservation release on departure.

cargo test --locked --release -p labello-api nine_client_polling_performance -- --ignored --nocapture

LABELLO_BENCH_IMAGES and LABELLO_BENCH_ROUNDS override the default image and cycle counts. Use at least nine images per round. Output contains only aggregate fixture size, workload counts, process CPU seconds, logical read bytes, process peak RSS, wall time, and route mean/p95 microseconds. Peak RSS includes fixture creation and warming. Logical reads include filesystem-cache hits and do not measure physical disk throughput. The test requires Linux /proc and getconf.

For a before/after comparison, copy the same test module and its mod declaration into an isolated checkout of the comparison commit. Build both release test binaries, then run each on the same otherwise idle machine with identical parameters. Record the base and changed SHAs, hardware, filesystem, Rust version, and full aggregate output. Repeat when scheduling variance obscures claim or submit latency. Keep fixtures out of commits; temporary data is removed when the test finishes. A smoke run with 100 images and two rounds checks the procedure, not the production-scale performance claim.

⚠️ **GitHub.com Fallback** ⚠️