6.2 KiB
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
:5182predates 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 withoutHQ_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.tsand the APEX importer CLI use an unconditionalopenDb()and therefore can never reach Postgres — they will always target SQLite.gmail-connect.tsis 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:
bulkSetMilestonecouples 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 as0rather than rejected. - Two
/client-modules/:id/milestoneshandlersawaitoutside theirtryblock (not reachable with attacker-controlled input, and not a regression). DraftInput.sourceis a free-form string taken from the request body — any authenticated user can tag a documentmodule_renewalorbulk_sms_topup. Provenance label only, no money impact; a union type would give a compile-time guard.ClientDetailstill callsgetModules/getTicketTypeson 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.tsandmigrate-pg-to-pg.tsare untracked working files.apps/hq/schema.sqlis a stale generated snapshot (dated 2026-07-10) that no longer matchesdb.ts, which is the source of truth. It does not even containproject_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.