--- name: coding-quality-workflow description: Use when starting coding, debugging, reviewing, landing, or inheriting agent-written work. Captures Alex's development tool preferences, root-cause-first workflow, native Hermes file/edit tools, build/deploy verification, and explicit reconciliation of Codex/subagent/prior-session handoff claims before trusting them. version: 1.0.4 author: Hermes Agent license: MIT metadata: hermes: tags: [coding, workflow, verification, handoffs, git, debugging, tooling] related_skills: [systematic-debugging, requesting-code-review, github-pr-workflow, subagent-driven-development, coding-workflow, verifying-agent-handoffs] --- # Coding Quality Workflow ## Overview Load this umbrella for most coding tasks. It captures Alex's preferred tool chain and the verification discipline needed when building, debugging, deploying, or inheriting work from another agent. The absorbed skills are preserved under `references/absorbed/` so the original detailed rules remain available. ## When to Use - Editing files in any app/repo. - Debugging bugs, test failures, deploy failures, or production regressions. - Reviewing code, running tests, or preparing a PR/commit/deploy. - Continuing from a Codex/Claude/Hermes subagent/prior-session handoff. - Reconciling claims such as “implemented,” “verified,” “build passes,” or “did not commit.” ## Alex's Tool Preferences Use native Hermes tools by default: | Task | Preferred tool | |---|---| | Read files | `read_file` | | Search code | `search_files` | | Edit existing files | `patch` | | Write new files | `write_file` | | Run git/build/test/deploy commands | `terminal` | Use Claude Code MCP only if Alex explicitly asks in the current session. If an MCP server is unreachable, do not keep retrying; fall back to native tools. ## Root-Cause-First Coding Loop 1. Load relevant domain skills before editing. 2. Inspect current repo state and files. 3. Reproduce/understand the issue or acceptance criteria. 4. Make the smallest coherent change with `patch`/`write_file`. 5. Run the established build/test/smoke commands. 6. Deploy/restart only after build/tests pass when deployment is part of the task. - If the user says not to deploy/restart/disrupt running processes, do not run PM2/systemd restarts or other live reloads. Also treat frontend build outputs carefully: in apps that serve `dist/` directly, `npm run build` can update live static assets even without a process restart. Prefer syntax checks/tests, or build in a temp copy when a zero-deploy guarantee matters. 7. Summarize root cause, changes, and verification. - Lead with the operational answer Alex needs now: what is fixed, what is live, what remains unverified, and the single next action. If a real-browser retest is the only remaining gate, say that in the first sentence. - Do not bury status beneath a long architecture or documentation explanation. Put requested background after the status, and keep it compact unless Alex explicitly asks for a deep explanation. - A minimal `?` after a long technical update is a correction signal: answer with the direct state and next action rather than repeating the full narrative. ## Authenticated Product Audits When Alex asks “have a look at this app and tell me what you think,” review the live authenticated product, not just the repository. Combine product intent, process/health evidence, non-sensitive database reality, desktop/mobile screenshots, critical decision flows, and console errors. For approval-driven agent systems, test whether compact queue actions can bypass full inspection and whether blockers, targets, spend, and decision controls are visible on mobile. Measure DOM widths before and after drawers/modals; full-page screenshots can include offscreen transformed panels and falsely suggest viewport overflow. Conversely, source/DOM contract tests can miss real clipping in translated or scaled onboarding/canvas scenes: capture the target viewport after the actual state transition and verify both the prior-step node and current workspace remain fully visible, connected, and non-overlapping. Treat a zero-test safety-critical app as a material risk even when health and browser smokes pass, and interpret high PM2 restart counts using current uptime, unstable restarts, exit code, watch mode, and logs rather than the counter alone. See `references/authenticated-app-product-audit.md` for the credential-safe Playwright pattern, layout measurements, approval-safety checklist, and product-critique structure. For live domain/canvas graphs, inspect the backing entity inventory before accepting a visual relationship as architecture. Distinguish coordinator control edges from database/service context inputs, represent every live product and its deployment, derive responsibility links from real project relationships, and prove the result in the authenticated canvas. See `references/domain-canvas-relationship-semantics.md`. ## Handoff Reconciliation Rule Before building on another agent's report, reconcile it against the repo. Agent self-reports are not facts. ### Disambiguate parallel sessions before answering “where are we?” When several sessions concern the same product, do not select a handoff from generic product-name recency alone. Search for the user's exact distinguishing nouns and flow (for example, a specific business, screen, example, or failure), inspect the matching session's ending, and compare it with current on-disk state. If the user corrects the session, acknowledge the mismatch plainly, discard the unrelated lane, restore the correct task list, and continue from the corrected checkpoint. Keep parallel lanes visibly separate—for example, product-flow development versus infrastructure provisioning—so a status update never substitutes one for the other. For interrupted multi-phase goals, continuity is part of correctness: recover the preserved task list/session history, inspect surviving worktrees and uncommitted files, state exactly what completed versus timed out, and resume from disk. Do not answer a check-in with a generic “still here” while silently dropping the active goal. When a background worker times out, treat timeout as unknown/incomplete rather than failure: inspect `git status`, diff/stat, untracked files, tests, and commits before redispatching. Salvage usable uncommitted work and finish it directly. If the assignment exceeded the worker ceiling, decompose the next pass into one subsystem plus focused tests plus one commit; do not repeatedly send the same oversized goal. Independently rerun the focused and canonical suites before accepting or integrating the salvaged commit. **Messaging-thread continuity pitfall:** a Telegram/Discord thread can remain visually continuous while Hermes attaches a fresh or rotated backend session. If the user sends a minimal ping/name after an active multi-phase build, do not greet them as a new conversation. Search recent session history for the active goal and its last verified checkpoint, then reconcile live repo/process state before replying. If the user pastes a prior checkpoint, use it as a locator: recover the original session and inspect any later background completions before claiming that pasted checkpoint is current. **Alex's expedited-development preference:** when Alex asks to move faster, stay lean, or avoid overbuilding, shorten ceremony—not destructive or data-integrity gates. For routine UI/style changes, use focused tests, the affected production build, and one real target-viewport smoke; do not automatically add a full-suite rerun or independent review. Reserve canonical full suites, disposable lifecycles, and independent/security review for changes that touch authorization, tenancy, billing, identity, migrations, persistence, or release infrastructure. Do not dispatch an optional background review that will finish after deployment: either skip it under the lean rule or explicitly wait for it as a release gate. Never rerun the full suite after every small fix or spawn repeated micro-reviews. State which checks were intentionally collapsed and which hard gates remain. When Alex reviews a long remaining-work list and says he can test the product himself, immediately collapse it into a release-critical path rather than defending the test plan. Default to one focused regression for the unresolved risk, one production build, one live identity/health probe, and one defining read-only feature probe. Move exploratory UX, browser matrices, repeated full suites, and broad optional audits out of the release gate. Continue through merge and deployment in the same task unless a real safety blocker appears; do not stop after merely presenting the shorter plan. **Asynchronous review gates remain gates.** For security-sensitive authorization, tenancy, billing, identity, migration, or production work, dispatching an independent reviewer is not equivalent to receiving approval. Local commits, backups, and deployment-envelope preparation may continue while review runs, but do not fast-forward a protected/default branch, label an artifact as the release candidate, or perform the gated live mutation until **every dispatched required reviewer** has returned and its verdict is reconciled. A narrow review that says “no blockers” cannot overrule a broader security review that is still pending; review scope is part of the evidence. Feature-branch pushes may continue when useful, but keep `main`/release integration gated. **A review approves one exact staged revision, not a moving worktree.** Before dispatch: 1. Run `git status --short` and account for every modified, deleted, and untracked path. Plain `git diff` omits untracked files. 2. Stage only the coherent owned files; never use broad `git add -A` in a dirty/shared checkout merely to make the reviewer see everything. 3. Inspect `git diff --cached --name-status`, `git diff --cached --check`, and the complete cached diff. Split large reviews with `git diff --cached -- `—not an unstaged diff. 4. Optionally record a digest of the cached patch when several reviews overlap. 5. If any source/test file changes, is newly staged, is rebased with conflicts, or is regenerated after dispatch, the prior verdict is stale. Restage, rerun focused verification/static scans, and obtain a new review of the current cached diff. Ignore late verdicts for superseded revisions. 6. Before commit, prove the cached file list still matches the reviewed set and that reviewed files have no unstaged changes. Commit the reviewed index directly; do not append an unreviewed `git add -A` at the commit step. When the reviewer can access the shared repository, instruct it to inspect the absolute repo path and exact `git diff --cached`; otherwise embed the complete cached patch as inert data. Do not rely on unsupported delegation arguments copied from older examples—use the current `delegate_task` schema. A reviewer that saw only tracked/unstaged files, or a review dispatched before new support files were staged, does not satisfy the gate. **Sequence required reviews before the finish line.** Start a required asynchronous review after the coherent diff and focused regressions, before the final canonical suite, immutable artifact build, and deployment sequence. This overlaps useful work with the review and prevents an avoidable status-only turn after the user has said “continue and complete.” If review cannot be dispatched early, use a bounded synchronous review before beginning final release work rather than introducing a late idle gate. When reviews overlap, maintain a small ledger: reviewer, scope, status, verdict, findings addressed, regression added, re-review status. If any late asynchronous review returns blockers after a commit or push, immediately classify that revision as unsafe-to-deploy, halt release work, add direct adversarial regressions for every finding, and land a follow-up only after focused + canonical verification and clean re-review. Do not summarize the earlier revision as release-ready merely because production was not yet changed. An alternate reviewer that fails to authenticate, reaches a turn budget, or produces no verdict does not satisfy the gate. If a turn/tool ceiling arrives first, report the pending review plainly and resume when its result re-enters the session rather than silently converting “pending” into “passed.” **Report release status on three separate axes: implementation, deployment, and assurance.** Never alternate between “complete” and “pending” without naming which axis changed; that forces the user to ask “where are we then?” Lead with one unambiguous sentence such as: `Implementation complete; production live; independent security approval pending.` A timed-out reviewer is **no verdict**, not a pass or finding. If that review was required, replace it with a narrower live reviewer before deployment; if production was already changed, label the state `operationally live, assurance incomplete`, keep the exact rollback boundary visible, and do not call the release fully complete or independently approved until the replacement verdict is reconciled. Test counts, canaries, and direct self-review are useful evidence but do not silently substitute for an explicitly required independent review. For entitlement changes, a representative route sample is insufficient. Enumerate every human API, agent/gate API, module router, entity-ID mutation, null/default scope, legacy login path, and startup/seed path. Test persisted entity ownership—not caller-supplied `project_id`—and inject foreign/portfolio fixtures into disposable state to prove denial. See `references/security-sensitive-entitlement-review-gates.md`. **“Finish faster” means continue, not pause.** Frustration about duration, security ceremony, reviews, or repeated authorization checks is a workflow correction: freeze scope to the single release blocker, make the smallest repair, run its focused test, then run one final real lifecycle. Do not silently reinterpret frustration as a stop request, move active tasks back to pending, or leave the user assuming background progress continues. Only explicit language such as “stop,” “pause,” or “do not continue” ends execution. When Alex says “done, done, done,” collapse optional review/hardening immediately and drive the already-authorized disposable path to completion. For long tool-driven turns, progress continuity is part of the deliverable. Report natural checkpoints (artifact built, lifecycle gate reached, blocker found, final purge) rather than narrating every command. If execution must stop because of a turn/tool ceiling, say plainly that work is paused and why; otherwise leave a real continuable worker or scheduled work-and-report loop. Never imply work is still advancing when no process or job is active. Minimum checks: ```bash git status -sb git log --oneline -15 git stash list ``` Then for each claim: - Claimed file exists? Read/diff it on disk. - Claimed route mounted? Search for the import/router/app.use wiring. - Claimed migration applied? Check DB/schema/migration runner. - Claimed build/test passes? Run it yourself. - Claimed commit/push status? Check `git log @{u}..HEAD` and remote status. Only trust the repo, DB, live route, or command output you verified. ### Dirty shared registries: stage only the owned entry When a shared tracked registry contains unrelated edits from other agents, do not stage the entire file merely because your deployment added one entry. Prefer `git add -p` for an isolated hunk; when edits overlap, construct an index-only blob from the exact `HEAD:path` text plus the owned insertion, then inspect the full cached diff before committing. Avoid parsing and reserializing the whole registry because Unicode escapes, indentation, and newline normalization can create broad formatting churn. Preserve every unrelated working-tree edit and verify it remains unstaged after the commit. See `references/index-only-staging-dirty-shared-files.md` for the deterministic recipe and recovery path. For agent-written safety-critical adapters, manifests, projections, or disposable harnesses, rerunning the worker's tests is necessary but not sufficient. Perform a semantic boundary inspection for raw secrets in temporary files, existence-only reconciliation, incomplete tenant/operation/resource ownership checks, arbitrary PID signaling, implicit coercion or `JSON.stringify` on hostile objects, fragment-only projection validation, and test names that claim branches they never execute. For authorization helpers, enumerate every actor-scope form the system accepts—portfolio, project, module/resource-bound, empty, malformed, and unknown—and prove non-portfolio scopes cannot fall back to a caller-supplied project/tenant ID. For filesystem-serving boundaries, lexical `path.resolve` containment is not sufficient: malformed percent-decoding must fail closed and symlink targets must be checked with canonical/real paths before serving. When Node tests share imported singleton state (for example a cached SQLite module despite query-string cache busting on its parent), assert state deltas/no-new-row behavior or isolate the process; do not assume each subtest starts from a globally empty store. Audit acceptance verbs literally: “concurrent” must use overlapping operations and protect shared read-modify-write state; “both restore independently” must destroy/restore each side while holding neighbor payload and routing evidence invariant. Add direct regressions for each discovered gap, then rerun focused + canonical suites and live/default-state/process invariants before accepting the handoff. The reusable multi-tenant version of this review is in `applied-agent-platforms` → `references/durable-multi-tenant-lifecycle-proofs.md`. ### Long-running goal and progress-update loops When Alex asks for periodic updates until a multi-phase goal is done, an update schedule must preserve **work continuity**, not merely report an unchanged branch: 1. Make each scheduled prompt self-contained: repo/worktree, branch, completed phase/commit, active acceptance criteria, safety boundaries, and verification commands. 2. Have the durable loop inspect git/worktrees/processes before acting so a fresh session does not duplicate or conflict with active work. 3. Either use a quiet worker plus a separate reporter fed from the worker's latest output, or use one bounded work-and-report job when serialized execution is guaranteed. A reporter-only job is insufficient if no other process is advancing the goal. 4. Every update should lead with **State → Recent verified work → Blockers → Next direction**, including test counts and commit IDs only when verified from disk. 5. Keep production deployment/live-state mutation behind the explicit later gate even if the loop can edit, test, commit, and push autonomously. 6. Stop/remove the recurring update job once the completion contract is met; completion output should clearly say the goal is done rather than continuing no-op updates. 7. When the user asks whether development is still happening, verify all three signals before answering: an active process/delegation/work job, recent repository or filesystem activity, and a concrete unfinished checkpoint. A reporter cron is not an active worker. If no worker exists, say plainly that development stopped, remove any misleading reporter, and launch a real bounded worker before claiming it is active again. Immediately poll newly launched background processes once; a successful launch call alone does not prove the coding agent authenticated or stayed alive. For an interrupted user check-in such as “status?” or “what happened to the goal?”, answer from the preserved task/repo state immediately, then resume execution. Do not make the user reconstruct the goal. Full preserved details: - `references/absorbed/coding-workflow/SKILL.md` - `references/absorbed/verifying-agent-handoffs/SKILL.md` ## Immutable policy registries, product manifests, and operation ledgers For registries that drive authorization, provisioning, provider policy, product boundaries, or lifecycle, use strict allowlisted schemas and detached immutable descriptor copies. Do not treat `Object.freeze()` as sufficient: `Map`, `Set`, dates, typed arrays, and class instances can retain mutation methods or shared references. Preserved profile functions are another boundary: freeze private policy constants, return fresh arrays/objects on every call, and prove that source-profile mutation, sibling-channel mutation, and prior-result mutation cannot contaminate future config/scaffold output. Add adversarial TDD probes for alternate unknown keys, duplicate/unknown enums, source mutation, returned-value mutation, unsupported non-plain objects, closure/shared-array contamination, package exports, and composed naming contracts. Keep spec-compliance and security/code-quality reviews as separate gates; a spec PASS never overrides a reproducible security REQUEST_CHANGES. For file-backed platform operation ledgers or idempotency journals, do not stop at happy-path replay tests. Probe exact product-registry set drift, contract-validator bypasses, forged fixed profile/provider fields, policy-invalid existing ledger records, UUID/operation-id collisions, duplicate JSON object keys before `JSON.parse`, max-record/oversize bounds, and cross-instance concurrent writers pointed at the same file. A per-instance promise queue is not a resource lock; if two store instances can both report success while one write is lost, the ledger is not durable enough for product operations. For shared internal control-plane APIs, keep a fixed platform-authority contract separate from mutable engine manifests. Enforce exact order-insensitive product/profile/provider sets independently in the router and direct store boundary; a registry superset must never silently expand lifecycle authority. Authenticate before body parsing, require raw/normalized authority-header value agreement, keep the first API phase acceptance-only, and wrap error classification itself against hostile getters/Proxies so failures cannot escape as HTML or attacker-controlled text. See `references/product-profile-manifest-registry.md` for the complete validation, mutation-probe, compatibility, and review recipe. See `references/platform-operation-ledger-spec-review.md` for adversarial probes and reporting guidance for operation ledger/idempotency-store audits. See `references/internal-control-plane-authority-boundaries.md` for the reusable fixed-authority, fail-closed authentication, durable ledger, side-effect-free internal API, privileged verifier, operational consistency, and legacy-provisioner extraction patterns. See `references/rollback-safe-provisioning-planners.md` for extracting monolithic provisioners through characterization, pure deterministic planning, fail-closed resource preflight, full accepted-operation binding, scheduler product/profile gates, plan-driven allowlisted effect dispatch, evidence-based compensation, ownership-aware recovery for every transition acknowledgement, descriptor-safe dependency/scheduler boundaries, artifact parity, and staged executor adoption. ## Durable architecture/docs consistency audits When auditing plans, ADRs, README files, in-repo skills, or `.hermes/plans` for contradictions with a corrected architecture decision, find the newest accepted superseding decision first and classify every durable surface as preserve, preserve-with-qualifier, amend, or supersede. Separate historical roadmap evidence from active implementation instructions, and return patch-ready replacement wording rather than a broad prose summary. Include dirty-work warnings if the repo already has untracked/modified files. See `references/durable-architecture-docs-consistency-audit.md` for the full Caduceus per-tenant-stack audit pattern and output table. ## Verification Checklist - [ ] For deployment-only changes in a repo whose canonical `make lint` fails in unrelated existing source files, do not hide the failures with broad ignores or claim full suite success. Report the canonical blocker, then run targeted verification for the files actually changed (e.g. shell `bash -n`, nginx `-t`, PM2 status/port ownership, health/API/public HTTPS/static asset/browser smoke). - [ ] Skill/domain context loaded before editing. - [ ] Git status reviewed before and after changes. - [ ] After the final test/build, run `git status --short` again before staging: verification can regenerate `__pycache__`, `*.pyc`, coverage, snapshots, compiled assets, or temp databases after an earlier clean scan. Remove derived artifacts or add a narrow ignore rule, then inspect `git diff --cached --name-status` and run `git diff --cached --check` immediately before commit. Treat the staged file list—not the pre-test diff or reviewer snapshot—as final truth. - [ ] Files changed are the intended files. - [ ] Tests/build/smokes run with project-established commands. - [ ] For greenfield Python API + Vite SPA work, separately prove package installation, shared UI/API contract, production frontend build, exact ASGI process command, localhost smoke, and browser console/render. See `references/greenfield-python-vite-release-gates.md`. - [ ] Before running tests from a live checkout, inspect whether they override every mutable runtime path (jobs, journals, entitlements, QC queues, DBs, uploads, generated reports). Prefer a centralized `*_STATE_DIR` contract that maps all mutable registries into one unique temp directory, fail closed in `NODE_ENV=test` if any path points at live repo `data/`, and snapshot/hash live files before/after. Capture exact failures to a temp log so output truncation cannot erase the evidence. See `references/forensic-live-system-takeover-audit.md` and `references/test-mutable-runtime-state-isolation.md`. - [ ] If the environment or post-turn verifier reports “unverified” after edits, do not argue with prior build output. Rerun the verifier-requested canonical command immediately (for Python, prefer `PYTHONDONTWRITEBYTECODE=1 .venv/bin/pytest ...` when available so verification does not leave fresh `__pycache__` churn). In monorepos, do this even when the changed slice is backend-only and the requested command is a frontend/root build such as `pnpm run build`; then rerun focused tests for the changed behavior so the final report includes both canonical build evidence and targeted behavior evidence. If no canonical command exists, create and run a focused temporary `/tmp/hermes-verify-*` script using an OS-safe tempfile path, verify the changed behavior directly against source and/or the deployed artifact, remove the script afterward when possible, and label the result explicitly as **ad-hoc verification** rather than suite green. - [ ] When rerunning canonical JS/Node verification under shell pipelines, preserve the exit code with `set -o pipefail`/`bash -o pipefail -c 'npm run test > /tmp/... 2>&1'` rather than `npm test | tee ...`, which can falsely return success from `tee`. If a repository has unrelated dirty or untracked tests and the canonical glob (`node --test test/*.test.mjs`) picks them up, report the canonical failure as blocked by those exact paths, then separately run focused tests for your changed files and, when useful, the tracked baseline plus your new test (`node --test $(git ls-files 'test/*.test.mjs') test/new.test.mjs`). Do not call that a full-suite pass; label it targeted/tracked-scope evidence and keep the dirty-tree warning prominent. - [ ] For Vite/PWA apps that serve `dist/` directly, remember `npm run build` can update live static assets even without a process restart. Prefer syntax checks/tests, or build in a temp copy when a zero-deploy guarantee matters. For dirty live checkouts where a verifier explicitly requires `pnpm run build`, run the canonical build in an rsynced temp copy (excluding `.git`, `node_modules`, served `dist/`, live `data/`, `.env`, and tenant runtime mounts), then run focused non-mutating checks in the live repo. See `references/tenant-appliance-temp-build-verification.md`. - [ ] For stateful tenant appliances, source tests and a successful compilation are not release proof. Run the lean source → canonical controller → real image → one disposable lifecycle sequence, and verify non-root script readability plus writable staging under read-only roots. See `references/tenant-appliance-release-canary-gates.md`. - [ ] For standalone Hermes plugins, unit tests with a fake context are not compatibility proof. Copy the plugin into a temporary `HERMES_HOME/plugins/`, enable it in that temporary `config.yaml`, and invoke Hermes' real `PluginManager.discover_and_load()` without adding the plugin directory itself to `PYTHONPATH`. Assert the plugin is enabled with no loader error, the exact expected tool names/count, and qualified bundled skills. A test-only `PYTHONPATH=` can mask invalid absolute imports that fail under Hermes' namespaced directory loader; package-root imports must work as `hermes_plugins.`. - [ ] For Vite/Express apps, distinguish static rebuild from server-route deployment: new or changed `server.mjs` routes require a PM2 restart/reload, then localhost and public-route smoke. A live 404 for a just-added route often means the old PM2 process is still running. - [ ] For self-hosted Next.js apps using `output: "standalone"`, verify `/_next/static/chunks/*.js` directly after build/restart. If chunks return 500 while HTML returns 200, copy `.next/static` into `.next/standalone/.next/static` and `public` into `.next/standalone/public` before starting the standalone server. See `references/next-standalone-static-assets.md`. - [ ] Handoff claims are independently verified before action. - [ ] User-facing summary distinguishes facts verified by tools from assumptions. - [ ] If no canonical test/build exists after code edits, create and run a temporary `/tmp/hermes-verify-*` ad-hoc verification script against the changed behavior, delete it afterward, and explicitly label the result as ad-hoc verification rather than suite green. - [ ] For VPS jobs with bulky artifacts/downloads/scans, default mutable work dirs to the attached `/data` volume when available, keep `WORK=...` override behavior, and verify Docker mounts/active writes. See `references/vps-bulky-artifacts-data-volume.md`. - [ ] For crawler/downloader/quarantine pipelines, make the ad-hoc verifier exercise the full local flow with a temporary HTTP fixture: robots-aware discovery → detail-page resolution → authorized download → scan/report assertions. See `references/download-pipeline-ad-hoc-verification.md`. - [ ] For bounded multimodal extraction adapters, TDD tiny synthetic fixtures for every supported route before implementation, then assert bounds, public storage provenance, structural coordinates, and absence of raw local paths/binary payloads. See `references/bounded-multimodal-extraction-adapters.md`. - [ ] For public Viewer/catalog pages generated from quarantine/download pipeline output, verify the served `/p/` content, not just `npm run build`: assert expected catalog markers are present and raw paths/direct download hosts/source-page URL patterns are absent. Use non-alarming scan labels such as “Needs active-content review” unless malware is confirmed. See `references/public-catalog-viewer-from-quarantine-corpus.md`. - [ ] For licensed STL/OBJ assets turned into custom Three.js Viewer pages, preserve and validate the original, generate separate desktop/mobile GLBs, and serve them through a public page-scoped `/p/...-assets` route. Loader completion, HTTP 200, bounds, draw calls, and a “model online” label are prerequisites—not proof that pixels reached the canvas. Verify an untouched fresh navigation, a screenshot that visibly contains the upright mesh, and non-background WebGL canvas pixels; exercise every advertised mode independently. Console render/material/transform mutations are diagnostics and invalidate visual acceptance. Avoid material arrays on ungrouped geometry and never add overlay meshes during `traverse()`. For animation-loop exceptions, import-axis pose isolation, and end-to-end Lab transition QA, see `references/threejs-animated-lab-and-blank-canvas-debugging.md` and `references/threejs-react-transition-scenes.md`. For real graph-backed clouds inside a character, sparse LODs, and autoplay-to-manual camera controls, see `references/threejs-hermes-to-knowledge-cloud.md`. See also `references/licensed-stl-threejs-wireframe-viewer.md` and `references/threejs-blank-model-verification.md`. - [ ] For long-running download streams, catch `requests` exceptions around `iter_content(...)`, not just the initial GET. Log `stream_error`, remove `.part` files, continue the corpus, and verify with a fake broken stream plus subsequent good file. See `references/download-stream-error-recovery.md`. ## Finishing an Already-Implemented App Plan When Alex says “finish up” after a multi-phase app plan, treat it as a landing/deployment request, not an invitation to re-plan. Use a tight closeout loop: 1. Reconcile repo/process state first: `git status -sb`, recent commits, upstream equality, PM2 app/port ownership. 2. Run the project’s hard gates plus targeted tests for the plan’s core UX/API path. For chat-first mobile work, include mobile Playwright smoke and any model-backed extraction/security tests. 3. If verification passes and the worktree is clean/upstream is current, do not manufacture another commit. If there are intentional changes, commit/push before deploy. 4. Deploy/restart the exact PM2 process only after passing gates, then verify PM2 `online`, the expected loopback port owner, localhost health, public health, and public route status. 5. Use browser console/screenshot only as final live UX smoke, not as a substitute for tests. 6. Final summary should be factual and short: commit, tests passed with counts, PM2/health/public-route status, and live URL. Reference: `references/app-plan-closeout-checklist.md` captures a concrete Astral Hermes closeout example. Reference: `references/sqlite-wal-disposable-restore-rehearsals.md` covers approval-gated live SQLite backup rehearsals: current dirty-tree capture, online WAL backup, foreign-key parity, DB-referenced media reconciliation, inert loopback clones, full scheduler-interval proof, and exact owned cleanup before feature work or later cutover. Reference: `references/worktree-ci-gate-debugging.md` covers Express `sendFile` failures under hidden worktree paths, Playwright resource-boundary diagnosis, and truthful GitHub Actions setup for private sibling repositories. Reference: `references/forensic-codebase-takeover-audit.md` captures the no-side-effect forensic takeover audit pattern: reconcile git/worktrees/remotes/stashes, dirty sibling repos, source-vs-live claims, package/test/docs inventory, TODO markers, and redacted secret hygiene without editing/building/deploying or exposing secrets. Reference: `references/forensic-live-system-takeover-audit.md` extends that pattern for authorized live-system audits: evidence classes, session/git reconciliation, source-to-deploy parity, safe test-state isolation, dual-transport semantic parity, backup/restore proof, integration-state classification, and concise takeover synthesis. Reference: `references/caduceus-developer-run-status-surface-inspection.md` captures the no-edit Caduceus pattern for identifying least-invasive Developer queue/run-status/evidence surfaces: DeveloperDrawer tab, `/api/m/developer` routes, ReviewDetail `dev_workorder` detail, review decision hooks, canvas badge projection, exact data shapes, and Node test conventions. Reference: `references/caduceus-approval-to-development-dispatch.md` captures the Caduceus pattern where approved `dev_workorder` review items become durable Developer-owned dispatches: inspect gate/review/jobs/hooks/module first, use a singleton scanner job with idempotent workorder rows, and keep Avalon local dispatch behind a portable development-surface adapter contract. Reference: `references/caduceus-managed-hosting-foundation-slice.md` captures the bounded managed-hosting foundation pattern: tenant-owned SQLite domain/storage tables, server-side target normalization, no raw secrets in DB/config, deterministic admin-only `avalon-apps` target visibility, effective target resolution order, and focused tests plus canonical build verification when requested. Reference: `references/caduceus-zero-field-passkey-signup.md` captures the customer-facing central signup pattern: zero-field mobile passkey landing page, discoverable/resident WebAuthn routes, CSRF/origin checks, secure SameSite=Lax session cookie, metadata-only operator route, durable account-store progress refresh after coordinator ticks, source contract tests, and Spawn-disabled local smoke verification. Reference: `references/caduceus-adaptive-onboarding-seed-extraction.md` captures adaptive long-answer onboarding: model-extracted bounded briefs, full UI→HTTP→token→seed limit alignment, one provider call per visible turn, one semantic engine before and after passkey, a distinct natural enrichment prompt, prevalidated seed-before-WebAuthn persistence, partial-signup recovery, safe browser errors, compact status UX, and real model-backed verification. Reference: `references/caduceus-central-onboarding-reuse-real-ui.md` captures the no-edit/implementation mapping for making central onboarding reuse real tenant project UI instead of imitating it: extract project-only Shell chrome, add read-only injected `CanvasStage`, reuse real node faces, split `ChatPanel` view/container, map `ProjectSeed` to `CanvasGraph`, keep central off tenant canvas/chat endpoints, and preserve v3 seed handoff. Reference: `references/caduceus-role-tier-failing-test-survey.md` captures the no-edit Caduceus pattern for surveying existing central signup/passkey, tenant handoff, role/auth, profile isolation, Shell/ChatPanel, and portfolio UI tests before proposing exact failing tests for role/tier or entitlement features. Reference: `references/caduceus-tenant-tier-bootstrap-profile-audit.md` captures the no-edit mapping pattern for normal/pro/admin tenant tiers: separate central signup from tenant app, distinguish first-business bootstrap from Dreamer dossier approval, preserve handoff compatibility, keep normal-tier project-first UI/business-only Hermes profile behavior distinct from pro/admin portfolio behavior, and use additive migration/rollback semantics. Reference: `references/caduceus-central-account-roles-flow-map.md` captures the no-edit Caduceus mapping for introducing central account roles such as `normal|pro|admin`: exact central files/tables/routes, Spawn payload/env/handoff seams, existing tenant `admin|reviewer` auth collision risk, managed-hosting entitlement surfaces, and tests to update. Reference: `references/caduceus-canonical-canvas-chat-audit.md` captures the no-edit Caduceus canonical tenant canvas/chat audit pattern: latest-origin worktree selection, React Flow graph components, `/api/canvas` and `/api/chat` contracts, node/edge/table schemas, normal single-business shell/mobile chat behavior, Developer artifact reconciliation, and idempotent seed-manifest import risks. Reference: `references/caduceus-social-provider-publishing-parity.md` captures the strict-TDD Social publishing pattern: exact surface→connection→provider-asset token authority, direct Instagram Graph origin/version, explicit Reel/Story/Carousel intent, pre-write Facebook mixed-media rejection, Agent Gate/schema parity, capability-truthful scheduling UI, and fresh post-commit verification. Reference: `references/caduceus-onboarding-social-oauth-integration.md` covers reconciling parallel onboarding/Social worktrees onto the hardened descendant branch, provider-aware non-blocking OAuth actions, exact tenant callback origins bound into durable state, and safe first-time surface linking. Reference: `references/caduceus-chat-action-review-navigation.md` captures the Caduceus no-edit audit/fix pattern for coordinator chat action chips: `ChatPanel` bracket parsing, `Shell.chipAction()` panel routing, single-bracket quick replies, `panel:reviews` backward compatibility, exact `review:REV_ID` deep links, Inbox focus props, and server-side prompt/gate review-id contracts. ## Mobile/chat-first product polish lessons When Alex is testing a mobile/chat product, prioritize the feel of the first screen over feature completeness. If he says the UI is “too much information” or people have ADD, simplify aggressively: one small conversational element, one primary action, one small fallback. Avoid duplicate controls for the same action unless the user explicitly asks for both visible at once. ### Exact-interface onboarding and teaser rule When Alex says an onboarding, teaser, demo, or preview must use the **exact same UI** as the real product, stop polishing a hand-authored imitation. Reuse the canonical renderer, node faces, stylesheet, shell grid, and interaction grammar; adapt only the data source and transport. The latest visual direction wins over earlier acceptance tests: if he restores the original journey choreography, recover that implementation from Git and retain the shrunken intro node plus connector while preserving the current canonical canvas, three-answer shaping, passkey flow, and handoff. If he instead asks for a blank real canvas, the prior card must finish disappearing and unmount. Make any real navigation panel collapsible in the actual product and start it collapsed in onboarding instead of inventing onboarding-only chrome. For React Flow entrances, never animate the wrapper `transform`; sequence opacity/filter with a per-node delay and glow the child node so x/y positioning remains intact. Show the business name on the canvas as soon as the second answer is submitted while keeping the final server profile authoritative, but reject pronoun-led fallback candidates (`I`, `we`, `it`, `they`, etc.) so optimistic projection cannot stick a non-name into the frame. Reuse existing chat-chip/send behavior for channel selectors, and restyle/floating-position the authenticated chat without replacing its streaming/thread/action controller. When the shared app locks `html/body` scrolling, onboarding must own a bounded scroll container: use `height: 100dvh`, `overflow-y: auto`, and remove fixed workspace `min-height` floors. Prove both a short desktop viewport (roughly 1280×577: workspace bottom stays within the viewport) and a short phone viewport (roughly 390×667: the onboarding container has positive `scrollTop` reach), plus zero horizontal overflow. A standard tall-phone smoke does not cover this failure mode. A floating chat must be removed from grid sizing exactly once. Do not reserve both a shared shell's third column and an inset on `.rf-wrap`; Vite's final CSS cascade can preserve the generic `.app` columns even after a `chatwide` class is removed. Use a higher-specificity shell override, then prove real post-transition geometry in a browser. `width > 0`, source regexes, and successful builds are insufficient—a 112px canvas is technically visible but unusable. Measure workspace, stage, React Flow wrapper, project frame, viewport transform, and document overflow after graph expansion at desktop and phone widths. If the canvas mounted under a zero-width animated parent, mount/refit after final geometry exists. For provisioning-time chat, keep infrastructure status out of the transcript. Put one compact spinner/status label in the chat header; when readiness arrives, turn that header affordance into the explicit open/continue action. Do not also insert a verbose status card or redundant “saved, keep waiting” paragraph among conversational messages. Do not confuse the **minimum setup gate** with the end of the business conversation. A fast three-answer gate may start provisioning, but while the header says Preparing the Coordinator should proactively continue with one highest-value missing Business Brief question at a time. Project the question into the canonical persisted conversation after passkey creation—not as a client-only fake bubble—then model-extract each answer, skip facts already supplied in rich prose, and select the next genuinely missing field. Stop proactive questions when the brief is sufficiently useful or readiness arrives, and never let optional enrichment delay or disable **Open my Caduceus**. Verify the exact post-passkey transition; pre-passkey extraction tests alone do not prove continuation. Make the chat itself visibly foregrounded through a near-white dedicated surface, stronger border/shadow, and crisp assistant-card edges—never by dimming or shading the canvas. Use a separate high-contrast user-bubble palette and calculate WCAG contrast (target at least 4.5:1) instead of assuming the brand gold is legible. Apply this visual language to both onboarding and the canonical tenant chat, then verify the real browser pixels. See `references/caduceus-central-onboarding-reuse-real-ui.md` for canonical UI reuse and `references/react-flow-floating-overlay-layout-qa.md` for CSS-specificity, fit, geometry, selector, reduced-motion, and Playwright acceptance details. For a tiny context-free phone-style switch, remove visible plan cards/legend/explanation, keep only a small track/thumb, and preserve accessibility through a larger transparent hit target, `role="switch"`, `aria-checked`, keyboard focus, and fail-closed values. Keep provisional graph nodes truthful and distinguish preview-only recommendations from durable seed defaults. Use the lean order: acceptance contracts → shared visual seam → compile → focused browser proof → deploy; expand seed/import semantics only when continuity requires it. Do not consume the release budget on adjacent backend enrichment and then stop before browser QA or deployment. See `references/caduceus-central-onboarding-reuse-real-ui.md`. For visual explainers and generated-reference implementation, treat reference images as geometry specifications first and styling references second. If the user asks to make it look like GPT-image/design mockups, rebuild the diagram grammar/template when necessary instead of skinning the existing renderer. User frustration like “you just dressed up the same terrible diagrams” or “the features are there but the layout is not like the mockups” means the workflow failed at the structural level: stop, discard the unsuitable graph/layout, and recreate the reference’s node grouping, arrows, callouts, avatar/bubble placement, central canvas, and information hierarchy from scratch before reapplying colors/typography. When Alex is frustrated by diagram attempts, first build **static diagram examples** for review before wiring dynamic data or animation. Ignore surrounding fake UI chrome unless requested; copy only the diagram primitives (circles, trees, hybrid tree→gateway→circle compositions, arrows/brackets/cards). See `references/static-diagram-prototyping.md`. Escalation rule: if Alex rejects the same visual/diagram prototype repeatedly or says to delete/remove it, stop tweaking CSS/JSX immediately. Remove the rejected mode/code path, add a regression proving the rejected UI/selectors are gone, and research a new renderer/architecture before rebuilding. For artistic astrology topology diagrams, prefer a canvas scene-model approach. Current preferred first rebuild: **raw Canvas 2D + tiny local retained scene model + local geometry helpers**, with Remotion later for video/export. Alex rejected Konva/react-konva and D3 as too heavy-handed; do not propose them again unless he reverses that decision. Treat Alex’s screenshot critiques as geometry QA, not subjective commentary: if he says arrows look wrong, add concrete alternate examples when useful; if arrowheads are hidden under glyph cards, increase trimming/heads until they are visible; if a hybrid looks cluttered, simplify topology before adding ornament. For implementation details, use `html5-canvas-game-development` references `artistic-astrology-canvas-diagrams.md` and `raw-canvas-diagram-lab-examples.md`. See `references/rulership-diagram-prototype-reset.md`. For voice-first onboarding or intake flows: - Pressing record should toggle start/stop; after stop, replace the record button with a processing/spinner state until transcription/parse returns. - Use a narrow fast path for form intake (transcribe → parse → confirmation) instead of routing through the full chat/agent reading path. - Do not hand-roll regex/string parsers for semantic user data like natural-language birth data. Use a fast structured model call for extraction, then let deterministic code only validate shape/ranges, normalize schemas, redact, and report missing fields. - When a user corrects an architectural approach (“don’t use regex; leverage models/fast models”), update the plan/skill and replace the brittle layer instead of adding another parser patch. - Confirmation/edit forms should reuse the app’s established pickers/autocomplete controls where they exist; do not silently fall back to plain text inputs if the user has already approved better controls elsewhere. - Strip internal router/prompt/memory scaffolding from final chat output. Progress can be visible, but hidden method instructions, skill names, and memory-candidate notices must not become user-facing assistant text. - If the app tells users background chart/bootstrap work is happening, show a visible readiness indicator. A chat line like “I’m casting your deeper chart layers now” is not enough by itself; pair it with pending/ready status from the bootstrap API and an obvious next action when ready. - If Alex says Astral/Hermes chat should not use direct model-provider answer paths, treat that as an architectural requirement. Do not keep or add `web backend -> provider -> assistant answer` shortcuts for user-facing chat. First benchmark tenant Hermes latency by profile/model tier, then implement Hermes run profiles, immediate status/acknowledgement, and background jobs so UX stays responsive without bypassing Hermes. Keep utility model calls (semantic routing, extraction, transcription) separate from final assistant answer composition, and add regressions proving user-facing chat goes through tenant Hermes. See `references/astral-responsive-hermes-run-profiles.md`. - For mobile `Load failed` chat errors, separate live stream transport failure from stale local thread history. Add a POST/SSE → JSON fallback for true streaming transport drops, and scrub old `I could not reach Astral. Load failed` bubbles from persisted localStorage thread history without hiding real backend errors. - For fixed mobile chat shells/drawers, keep the body locked but make the actual sidebar content view independently scrollable. Constrain the drawer to `100dvh`, set the inner `.sidebar-view` to `flex: 1; min-height: 0; overflow-y: auto; -webkit-overflow-scrolling: touch`, and add a mobile Playwright regression that proves sidebar `scrollTop` changes while `window.scrollY` stays `0`. See `references/astral-chat-first-onboarding-lessons.md` for the session-derived Astral example covering minimal birth-intake UI, Magi/Mobiscroll pickers, Google Places location autocomplete, voice parse, and PM2 route deployment pitfalls. See `references/astral-model-backed-intake-extraction.md` for the model-backed extraction pattern that replaced brittle regex birth-data parsing. See `references/astral-chat-context-cache-phases.md` for completing chat-first context phases: chart bootstrap cache artifacts, AI-guided cached fast answers, Memory Library/detail sheets, reading artifact persistence, SSE/non-SSE route parity, and verification/deploy checklist. See `references/astral-chat-stream-fallback.md` for debugging mobile/in-app-browser `Load failed` chat errors and adding a streaming POST/SSE → JSON fallback without masking real backend failures. See `references/astral-mobile-sidebar-scroll.md` for the fixed-shell mobile drawer pattern: body locked, inner sidebar view scrollable, and a Playwright scrollTop/body-scroll regression. See `references/astral-background-task-status-ux.md` for the durable UX rule: whenever a chat app starts background work after a user action, show a visible status card with concrete steps, ready/failure state, and next actions instead of only saying “working in the background.” See `references/conversational-coordinator-action-flows.md` for action-driven setup chats: one decision at a time, launch without a redundant second send, persistent accessible progress through streaming, explicit panel handoffs, automatic resume after verified actions, project-scoped credential selection, and friendly transport recovery. See `references/caduceus-create-business-coordinator-backend-audit.md` for auditing/repairing Caduceus Create Business coordinator backends: durable chat turns, crash recovery, user interruption, backend option/choice state, and automatic continuation after non-user-blocked replies. See `references/authorized-collection-quarantine-pipelines.md` for building authorized bulk download/research pipelines with discovery manifests, practical pacing + rate-limit backoff, quarantined static malware triage, final catalogs, and ad-hoc verification. See `references/astral-durable-background-job-tracker.md` for the class pattern Alex requested after a confusing deep-reading flow: make slow chat/background work first-class persisted jobs, expose a tenant-level compact “Background reading sessions” tracker, reconcile across app reopen/thread switches, emit completion events, and verify against a fresh Playwright server when `reuseExistingServer` may be serving stale code. See `references/astral-admin-test-tenant-provisioning.md` for the admin-only QA pattern: one-click creation of a fresh test user + tenant, reusing the real provisioning path while copying local OpenAI/Codex OAuth credentials server-side without exposing tokens. It also captures the visibility pitfall where generated `test-*` tenants are intentionally excluded from beta/customer lists and therefore need a dedicated Generated test tenants section plus a Users tab backed by the entitlement ledger. See `references/astral-codex-auth-and-chat-failure-handling.md` for the Codex OAuth pitfall discovered in Astral tenant testing: test tenants can be marked authenticated while `auth.json` lacks runtime-usable tokens; copy both `credential_pool.openai-codex` and `providers.openai-codex.tokens`, smoke with `hermes chat`, and never save provider-auth failures as Readings. See `references/astral-post-onboarding-sidebar-bootstrap-audit.md` for auditing Astral post-onboarding sidebar population, KB summary sources, chart/bootstrap cache lifecycle, and common UX mismatches such as hidden `chart-bootstrap.json`, `Me` vs `People`, KB root drift, and generic Chart View vs saved natal chart expectations. See `references/astral-rulership-engine-mobile-polish.md` for mobile visual-explainer polish patterns: screenshot-driven QA, safe-area spacing, separating admin controls from feature controls, sizing diagrams around legibility, checking label/card bounding boxes, trimming connector arrows away from glyph cards, and hiding duplicate explanatory cards. See `references/visual-explainer-rebuild-from-reference.md` for the design-reference pitfall Alex corrected: recreate the reference's technical diagram grammar/geometry first; do not merely dress up an existing generic graph with the reference's colors. See `references/static-diagram-examples-first.md` for the follow-on correction: when reference-driven diagram work has gone off track, delete/replace the rejected mode and build a static gallery of diagram primitives first (mutual reception, 3/4/5/N circles, hierarchy tree, hybrid tree-feeding-circle), ignoring fake app chrome and avoiding lookup-table/old-engine scaffolding until Alex approves the grammar. ## Source-Backed Cultural/Text Viewer Apps When building apps around historical/literary/source material, treat “find the text/source” as a hard requirement to use existing authoritative sources, not OCR, AI generation, or local reconstruction. - If the user asks for original + readable/modern text, first identify the authoritative existing digital editions and their rights before coding the data layer. - Do not substitute IA OCR/DjVu, generated paraphrases, or “working text” unless the user explicitly approves that as a temporary placeholder. - Preserve the source’s actual order and start point. For facsimile books, skip scanner covers/binding/modern introductions if the requested work starts at a historical title page such as “Neuer before Imprinted.” - Prefer tabs/toggles for alternate editions of the same page/line when the user asks to switch views, rather than showing unrelated side-by-side panes. - Make source provenance visible in the UI and in the manifest: facsimile source, old-spelling transcription source, modern-readable source, license/permission notes, and whether the text is edited vs OCR. - If modern text cannot be found with the exact same page/line breaks, say that clearly and map only existing source lines as tightly as possible; do not create new modernized wording to fill the gap. - Search must query the complete sourced text, not labels/previews only. Before declaring search fixed, verify at least one user-provided example query and show the hit count/pages. - Default literary/source-text search should match whole words, not substrings: searching `light` must not match `delight`, `slight`, or hyphenated compounds unless the user explicitly selects an “inside words”/substring mode. Provide visible search modes when both behaviors matter. - Search must query the complete sourced text, not labels/previews only. Before declaring search fixed, verify at least one user-provided example query and show the hit count/pages. - Default text search should be whole-word / token-boundary matching, not substring matching. Add a separate explicit “inside words” / substring mode for pattern hunting so a query like `light` does not silently return `delight`, `slight`, or hyphenated fragments. - For poetry/play/source-text search, group results at the user’s semantic unit first (sonnet, scene, chapter, stanza), then include facsimile/page/leaf references as metadata. Do not make page-level hits the primary result when the user is asking “which sonnets/chapters contain this word and how many times.” - When extracting text from edited HTML transcriptions, preserve inline typographic spans as part of the word. Block tags (`div`, `p`, `br`, headings, table cells) may become spaces/newlines; inline tags (`span`, `i`, ligature/typeform markup) should usually collapse with no inserted spaces, or words like `ſpeed` can become `s peed` and break exact search. - When source editions use old typography, normalize intentionally for search (`ſ`→`s`, u/v variants where appropriate), but keep rendered/source text unchanged and verify against original-spelling lines. - If the app’s purpose includes word/letter pattern discovery, add an explicit equation/search panel rather than a plain keyword list: normalized query, letters-only form, letter count, A1Z26/cipher totals, occurrence count, semantic-unit counts, and page/line/sonnet references. - For mobile-first text/facsimile viewers, make primary surfaces edge-to-edge on phones, keep search/tabs thumb-friendly and optionally sticky, then add desktop breathing room without changing the mobile workflow. - For mobile source-text viewers, avoid persistent hero/intro copy once the user is working in the app. Keep the main viewport to search, tabs/viewer, and navigation; move provenance/page/source details behind a dismissible info overlay. - On mobile, put previous/next and the page-position slider together in one bottom navigation bar with iPhone safe-area padding. If fixed, add bottom content padding so the viewer is not hidden under the bar. - Compact search controls: put search modes such as whole-word vs inside-word into a gear/settings dropdown inside the search input area rather than a separate row when minimizing UI chrome. - For poetry search, distinguish semantic-unit summaries from occurrence navigation. The visible hit rail should be occurrence-level chips: if a word appears once in Sonnet 50 and twice in Sonnet 51, show `S50`, `S51.1`, `S51.2`, while deeper counts/details can live in an overlay or expanded panel. - Keep search available but collapsible on mobile: use a top drawer/handle that opens the full search controls and can collapse back to a slim query label so the text/facsimile remains dominant. - Highlight searched terms in every view the app offers. For edited text views, highlight matching lines or words using the same normalized matcher as search while leaving source text unchanged. For facsimile scans, avoid inventing OCR-derived word boxes unless coordinates are reliable; use page-level/line-level facsimile hit indicators as a safe bridge. - For source-text “equation” or numerology views, compute from the same semantic-unit search index as the main search UI. Repeated occurrences should become repeated factors unless the user explicitly asks for unique-only math (e.g. `speed` in Sonnet 50 once and Sonnet 51 twice → `50 × 51 × 51`). Preserve source-text display while allowing spelling aliases for search/math modifiers (e.g. `pyramids` matching quarto `pyramyds`). - Equation demonstrations should expose their arithmetic: calculated value, target/reference value, percent match, expandable factors for each word, modifier factors, conversion constants, and scale factors. If a video/screenshot row’s word-pair is unknown, scaffold the row as pending exact words rather than inventing a match. Reference: `references/source-backed-cultural-map-viewers.md` captures the class pattern for sensitive cultural/archaeological map viewers: approximate public-source pins, visible respect language, per-site modal views, provenance-backed image galleries, and no field-guide overclaiming. Reference: `references/source-backed-text-viewer-equation-views.md` captures equation/numerology view patterns for source-backed text apps. Reference: `references/source-backed-text-viewer-lessons.md` captures a compact session-derived checklist for facsimile/text-source apps. Reference: `references/source-text-search-indexing.md` captures implementation notes for semantic-unit search indexes, whole-word defaults, old typography normalization, and inline-HTML extraction pitfalls. Reference: `references/source-backed-text-viewer-search.md` captures search-mode semantics and HTML inline-typography extraction pitfalls for historical text viewers. Reference: `references/source-backed-text-viewer-mobile-ui.md` captures mobile UI patterns for minimal chrome, bottom navigation, info overlays, settings dropdowns, and occurrence-level hit rails. Reference: `references/source-backed-text-viewer-interactions.md` captures collapsible search drawers, occurrence rails, and safe highlighting patterns across text/facsimile views. Reference: `references/astral-chat-first-onboarding.md` captures Alex's Astral-style mobile onboarding expectations: tiny chat-first prompt, single record toggle, processing state, manual fallback, no duplicate controls, and no internal routing/prompt scaffolding in user-facing chat. Reference: `references/legacy-browser-app-revival-verification.md` captures the Freemix-derived pattern for reviving old browser apps: browser screenshots delivered as actual `MEDIA:` attachments, auth/session/socket ad-hoc verification, legacy Passport/nested-dependency pitfalls, Redis callback modernization, resource-panel layout checks, save/share public-page QA, and server-side snapshot/export debugging. Reference: `references/legacy-browser-app-revival-handoff.md` captures the follow-on handoff pattern for long-running legacy app revivals: keep source history separate from runtime deploys, create a dedicated remote project agent, write `/opt/project/agent/HANDOFF.md`, require an acknowledgement report, run token-cautious loops, and verify takeover state with `/tmp/hermes-verify-*` ad-hoc scripts. Reference: `references/product-profile-manifest-registry.md` captures the reusable pattern for adding and hardening a tested product→profile manifest registry to shared provisioning engines: policy-only manifests, strict schema allow-list validation, canonical provider id lists, detached frozen profile reads, container-prefix composition tests, TDD sequence, and documentation checklist. ## Source-Backed Knowledge Compilation When repairing document ingestion or building source-grounded wikis, separate raw extraction, compiled Markdown, and the semantic graph. Do not accept graph-node counts, minimum word counts, or agent QA summaries as proof of rich knowledge: inspect representative notes, detect repeated long prose, verify named procedures and worked examples, check citations/links/path leakage, and namespace note identity by corpus. Archive shallow artifacts before transactional replacement. For Alex, the acceptance artifact is **the complete document opened in the live product**—not a file count, graph count, endpoint response, or report that Markdown exists on disk: 1. Show one real canonical Markdown file early by quoting or attaching it. 2. Trace one identity end to end: source package → corpus → graph/card node → note endpoint → rendered document. 3. Make the expected surface itself clickable. If a concept is presented as a card, clicking the whole card must open it by mouse and keyboard; do not rely only on a tiny secondary button. 4. Render Markdown as readable typography, not raw `
` text. Hide frontmatter and technical paths/IDs while preserving headings, lists, tables, blockquotes, citations, and provenance.
5. Add an interaction regression that clicks the node/card and asserts a substantive heading from the full note appears.
6. Deploy and repeat the exact user path in a live browser. API success and DOM presence are prerequisites, not substitutes for the visible click flow.
7. Lead status/completion messages with the artifact and click result. Keep pipeline architecture and counts secondary unless requested.

For graph-node drawers, the compiled Markdown is the primary page. Auto-load it directly beneath the node title; do not put the short graph summary, sources, or connection list ahead of it and do not require a second “Read full note” click. Keep summary, sources, deduplicated connections, source metadata, related links, and provenance in a compact collapsed **About & connections** section. Use one drawer scroll, reset the collapsed state between nodes, and test that substantive headings are in the primary document region while metadata is secondary.

If Alex says “the Markdown exists but I cannot see/open it,” stop expanding adjacent scope. Do not spend the next update on activity systems, corpus counts, or architecture; finish and prove click-to-document first.

References:
- `references/source-backed-knowledge-compilation-and-replacement.md` contains the forensic comparison, editorial QA gates, corpus-aware integration, safe replacement, and lifecycle-aware product UX.
- `references/clickable-compiled-knowledge-acceptance.md` contains the focused artifact-first acceptance recipe and false-completion traps.

## Third-Party 3D Assets for Custom Web Viewers

When a user asks to “get” a 3D model and customize it, separate three acceptance states explicitly: **identified**, **embedded preview**, and **authorized archive acquired**. A provider iframe is not possession of the mesh, and an inspector/wireframe toggle is not a custom Three.js wireframe implementation.

Research candidates by subject synonyms, then verify provider API metadata (`isDownloadable`, license, creator, face/vertex counts, thumbnails) rather than trusting search snippets. Visually compare full-size thumbnails because scan completeness and silhouette can matter more than raw polygon count. Prefer museum/creator sources and CC0/CC BY for public or commercial reuse. Provider download endpoints may still require account authentication even when metadata says downloadable; use the official flow and never replace it with protected-stream extraction.

If the archive is not yet acquired, it is useful to publish a clearly labeled interactive embed preview and show the selected thumbnail, but report the authentication/download gate plainly. Only claim the requested custom renderer after loading an authorized GLB/glTF/OBJ/STL locally, preserving attribution/license metadata, optimizing a copy, and verifying the live desktop/mobile result.

See `references/licensed-3d-asset-acquisition-for-web-viewers.md` for the complete acquisition, selection, licensing, conversion, and acceptance workflow.

## Common Pitfalls

- Trusting “build passes” or “smoke tested” from another agent.
- Asking the user to re-upload artifacts already present in the repo.
- Deploying after a fix without running the build.
- Using shell text tools for file reads/edits when native Hermes tools are safer.
- Treating a handoff zip or pasted status report as authoritative over git/on-disk state.
- Building a source-backed text app from OCR or generated text after the user asked to find existing texts.
- Starting a facsimile viewer at scanner covers/front matter instead of the historical work’s first page/title page when that is what the user requested.
- In Express 5 / newer `path-to-regexp`, `app.get('*', ...)` can crash with `Missing parameter name at index 1: *`; use a terminal fallback middleware such as `app.use((_req, res) => res.sendFile(...))` for SPA catch-alls.