# Known issues & pending work — HQ Ops Console _Last updated: 2026-07-21 · after the D38 Client Detail redesign landed on `main`._ This is the honest list of what is **not** done, what is **not** verified, and what needs a human decision. Everything here was found by review; none of it is speculative. Ordered by consequence. --- ## 1. SECURITY — production Postgres password is in the repo and on the remote **Status: open. Owner decision parked.** The production Postgres connection string (with password) is hardcoded in `apps/hq/src/db-pg.ts` (`HARDCODED_DATABASE_URL`), and it **is present on the `origin` remote** (`gitea.simssoftware.in`). It was introduced in commit `45cfb83` (then in `server.ts`) and later moved into `db-pg.ts`; both states have been pushed. A duplicate copy that had been added to `apps/hq/test/db-resolve.test.ts` was removed in `6f50799`. **Why it matters:** under D31/D32 every client portal/gateway password, module secret and client DB password is stored **in the clear**. This one credential is therefore the key to all of them. **The only real remediation is to ROTATE the Postgres password on the box.** Editing or reverting the file does not remove it from history, and history is already distributed. Secondary (after rotation), pick one: - move the value to a deploy-time env var / secrets file and revert the code to a placeholder — **note the AWS box currently has no `.env`, so this requires an ops change there first**; or - accept the hardcoded pattern and treat repo read access as equivalent to production DB access. --- ## 2. BLOCKED — the real data has not been touched yet **Status: blocked on the local Postgres password.** The following were prepared and tested but **never run against the real database**, because the local PostgreSQL 17 instance's credentials are not available to this session: - **Scrub of the "tecnostac" name from the real DB.** The earlier scrub ran against `data/hq.db` — a near-empty leftover SQLite file (2 clients), **not** the live Postgres. The live database very likely still contains the old company name, owner login and audit snapshots. - **The onboarding template migration** over ~167 real projects (`apps/hq/scripts/apply-onboarding-pipeline.ts`). - **Redeploy** — the server currently running on `:5182` predates all of this work, so the console in use does not yet reflect the redesign. Until this runs, the redesign is **code-complete but not live**. --- ## 3. NOT VERIFIED — things nobody has actually observed working - **The Postgres migrations have never been executed against a real Postgres instance.** Migrations `015`–`018` (`project_milestone.payment_id`, `.document_id`, `ticket.type`, `client_module.created_at`) are statically reviewed and mirrored from the SQLite schema, but the test suite's Postgres path is skipped without `HQ_PG_TEST_URL`. 5 tests are skipped for this reason. - **The Payments tab was not screenshotted at 768px, nor at 1440px in dark theme.** The rest of the redesign was verified in a real browser at 375 / 768 / 1440 × light + dark. --- ## 4. DEFERRED TO ROADMAP (owner call) - **`gmail-connect.ts` and the APEX importer CLI use an unconditional `openDb()`** and therefore can never reach Postgres — they will always target SQLite. `gmail-connect.ts` is a documented production runbook step. Deferred because that area is not in active use yet. --- ## 5. BEHAVIOUR CHANGES worth knowing - **Tables now scroll inside their own box, bounded at `70vh`, with a sticky header.** Previously the whole page scrolled and (at desktop widths) tables silently dropped their rightmost columns. Short tables are unchanged; long tables scroll internally. This affects every table screen. - **Adding a module no longer asks for credentials.** They are captured when the "Account creation" onboarding step is ticked, and remain editable from the module's Access panel. - **The coarse module status is now driven by the status line.** Ticking Installation / Training / Go-live advances it automatically; it never downgrades. --- ## 6. MINOR BACKLOG (real, non-blocking) From the review passes, not yet fixed: - `bulkSetMilestone` couples to a literal error string (`'Milestone not found on this project'`); rewording that throw would turn a skip into a whole-batch abort. Prefer a sentinel/typed error. - `project.stall_days` (the setting, as opposed to the `?days=` query param) is not range-validated; an absurd value produces a 400 rather than a crash, but it should be clamped for symmetry. - Whitespace-only `?days=` input is treated as `0` rather than rejected. - Two `/client-modules/:id/milestones` handlers `await` outside their `try` block (not reachable with attacker-controlled input, and not a regression). - `DraftInput.source` is a free-form string taken from the request body — any authenticated user can tag a document `module_renewal` or `bulk_sms_topup`. Provenance label only, no money impact; a union type would give a compile-time guard. - `ClientDetail` still calls `getModules` / `getTicketTypes` on load in some paths; only the duplicate fetch was removed. - Chip/button tap targets are ~23px high on mobile — below the comfortable minimum, but this is a pre-existing system-wide convention, not something this work introduced. --- ## 7. REPO HYGIENE - **`.claude-flow/` is tracked in git** (6 files of agent daemon state/metrics). It is scratch and should be removed from tracking and added to `.gitignore`. - `apps/hq/scripts/migrate-sqlite-to-pg.ts` and `migrate-pg-to-pg.ts` are untracked working files. - `apps/hq/schema.sql` is a stale generated snapshot (dated 2026-07-10) that no longer matches `db.ts`, which is the source of truth. It does not even contain `project_milestone`. --- ## How this list was produced The redesign went through per-task reviews during the build, then a whole-branch review, a four-lens audit swarm, a real-browser responsive red-team, and a review of the fix commits themselves. Each layer found defects the previous one could not — including a payment feature that was dead code, a deploy script that would have written to a phantom database while reporting success, and a route any authenticated user could crash the server with. Those are fixed. The items above are what remains.