Console self-service + account lifecycle + security triage (2026-07-08)
Batch picked by Efran from the readiness ranking: **#2 persona editors,
3 schema browser, #5 per-account chat history, #8 password reset, #7
Dependabot triage**. (#1 feedback loop, #4 knowledge backup, #6 error
monitoring stay on the roadmap as next-up.)
#3 — DB schema browser (/admin/internal/schema) — UI-only
Backends exist (/api/admin/databases, /api/admin/databases/[name]/tables;
agent returns {databases:[{name,description}]} / {database,tables:[]}).
Page: DB selector → table list with client-side search (hundreds of tables).
Tested pure helper filterTables(tables, q). AdminRail INTERNAL entry
"Skema DB". Read-only; no new backend.
#2 — Persona editors
Public surface (/admin/public/persona): new persona_preamble: string
(default "", max 4000 chars) on BOTH Config copies (public-web
orchestrator/config.ts + internal-web orchestrator-config.ts — the files
document they must stay in sync). compose.ts prepends it to SYSTEM_COMPOSE
when set (buildMessages gains optional persona arg; orchestrate passes
cfg.persona_preamble). Editor page PUTs through the existing
/api/admin/orchestrator route. Empty string ⇒ byte-identical behavior.
Internal agent (/admin/internal/persona): mirrors the provider-admin
pattern. New light dash/persona.py (file-path testable): load_persona()
reads PERSONA_CONFIG_PATH (default /data/persona.json, {"persona": str};
missing/invalid ⇒ ""). agents.py prepends persona to
_EFFECTIVE_INSTRUCTIONS at build. New app/persona_admin.py (mirrored ×2):
GET /persona, PUT /persona (≤8000 chars) — saves atomically then hot-swaps
dash.instructions / public_dash.instructions / reasoning_dash.instructions
live (same mechanism as /provider/reload-schema). internal-web proxy
/api/admin/agent-persona (GET; PUT = requireMutationSession + audit row).
Compose: agents gain named volumes (…-agent-data:/data) so persona.json
and the pre-existing provider.json survive recreates (today they don't —
latent bug fixed by this).
#5 — Per-account chat history (Akun)
Public chat runs through the in-app orchestrator; the old /api/threads/doc
(doc-RAG /sessions) doesn't see those turns at all. New app-local
ThreadStore (better-sqlite3 on PUBLIC_DB): public_threads(id=sessionId,
user_email, title, created_at, updated_at) +
public_thread_messages(thread_id, role, content, created_at).
/api/orchestrateroute: when a valid Akun cookie is present, tee the SSE
stream — accumulateevent: contentdeltas; ondone, persist user +
assistant messages fire-and-forget (never blocks/fails the stream). Tamu ⇒
no persistence, zero behavior change./api/threads/docGET: Akun cookie ⇒ list own threads from ThreadStore
(Thread{id,q,when,mode:'doc'}); no cookie ⇒ legacy upstream passthrough./api/threads/doc/[id]GET: Akun-owned thread ⇒ its messages (shape matched
to the sidebar's history loader); not owned/absent ⇒ 404.
#8 — Password lifecycle (no email transport — email reset stays blocked)
- Self-service change (public-web):
POST /api/auth/change-password
{currentPassword, newPassword≥8}— requires valid session cookie AND
current-password verify;PublicUserStore.resetPassword(new; bcrypt +
token_version++) then re-issues the cookie with the newtv. Sidebar Akun
section gains "Ubah kata sandi" dialog. - Admin reset (internal-web): staff console manages public accounts —
/admin/shared/public-userspage +/api/admin/public-users(GET list) +
[id]PATCH{action: reset_password|disable|enable}; reset generates a
temp password shown once to the admin. App-localPublicAccountAdminStore
reads the SAME/data/public.sqlite(both webs mountahu-shared_policy-data
at/data— verified). Mutations:requireMutationSession+ audit rows
(mandate). No cross-app import.
#7 — Dependency vuln triage (91 Dependabot: 4 critical, 33 high)
No gh/PAT in this environment ⇒ triage via pnpm audit (registry advisories)
for the JS workspace + pip-audit against both agents' requirements.txt.
Fix criticals/highs that are non-breaking bumps (direct bumps or pnpm
overrides); re-run suites; report what remains (major-version/breaking ones
deferred with reasons). GitHub's alert list can be reconciled from the
Dependabot UI afterward.
Constraints
Boundary (no cross-app imports; data-file sharing via the shared volume is the
established staff/public pattern), mandate (admin mutations =
requireMutationSession + audit), mirrors byte-identical, TDD, pnpm check +
agent pytest green, deploy via build-and-ship → staging, verify live.