Files
geo/progress.md
T
root 3cb15ab5a3
Desktop Client Build / Resolve Build Metadata (push) Successful in 32s
Backend CI / Backend (push) Failing after 8m13s
Desktop Client Build / Build Desktop Client (push) Successful in 24m4s
Desktop Client Build / Publish Client Artifacts to NAS (push) Successful in 32s
docs: update planning artifacts for platform challenge handling
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 22:01:54 +08:00

86 KiB

Progress Log

2026-07-14T18:45:00+08:00 - Local same-platform challenge circuit breaker started

  • User screenshot disproved the previous end-to-end assumption: after monitoring_collect:7995 hit doubao_challenge_required, later Doubao task 7989 still attempted collection on the same single account.
  • Added Phases 66-68 to reproduce the local queue race, add a platform circuit breaker, and verify it without changing other-platform scheduling or multi-account failover.
  • Initial direction: cancel/remove already leased or queued same-platform work locally as soon as the last usable account becomes challenged; do not wait for server bulk status propagation.
  • Refined the screenshot diagnosis: later Doubao row 7989 has attempt 0, so it was not proven to reach the adapter; it was bulk-marked failed with the original challenge error, which is still the wrong cancellation state.
  • Added two red regressions. Desktop scheduler fails because no platform-level pending-task removal function exists; server test fails because later tasks are explicitly set to failed with no skip_reason.
  • Phase 66 is complete and Phase 67 implementation is in progress.
  • Desktop scheduler/runtime circuit-breaker changes applied successfully. The first combined server patch failed context verification and made no server edits; implementation continues with smaller exact-context patches.
  • Scheduler/account-pool focused tests now pass. First typecheck found one redundant event payload field; the server regression reached the new skipped SQL and now only needs updated argument assertions.
  • Implemented local platform circuit breaking, atomic same-platform pending-task removal, manual probe/rebind recovery, model-wide monitor cancellation across account targets, canceled lifecycle events, and skipped monitoring follow-ups.
  • Focused desktop tests pass (3 files / 12 tests), desktop typecheck passes, focused server authorization-cancellation tests pass, and git diff --check passes.
  • Full verification passes: desktop 31 files / 213 tests, desktop typecheck, desktop production build, and server-wide go test ./....
  • Final diff review found no debug instrumentation, whitespace errors, or unintended projection changes.
  • Phase 68 is complete. Live captcha triggering was not repeated against the user's account; the screenshot sequence is covered deterministically at the local scheduler and server terminal-state boundaries.
  • Root cause: follow-up tasks were already prevented from adapter execution in the shown case (attempt 0) but were bulk-recorded as failed with the original challenge message, and the client lacked immediate platform-wide queue purging. The fix addresses both the misleading state and the local race.
  • Planning completion check still reports historical unfinished phases 11/12, 17, and 48/49 from earlier roadmap work; current follow-up phases 66-68 are complete and unrelated roadmap state was preserved.

2026-07-14T00:00:00+08:00 - Human-verification failover and alert hardening started

  • Added Phases 63-65 for challenge reproduction, account-scoped failover, model-level cancellation, and desktop system notifications.
  • Recovered the latest multi-account context: same-platform account pooling already exists, while Doubao challenge handling currently blocks the platform at admission and surfaces only activity/page state rather than a guaranteed OS notification.
  • Added runtime-system-notification.test.ts for danger alerts, forced account-challenge alerts, per-account deduplication, and notification failure isolation.
  • Red test confirmed the missing seam: Vitest fails because runtime-system-notification does not exist yet.
  • Implemented the Electron-free notification policy/sink with 30-second per-error deduplication and exception isolation.
  • Wired all danger runtime activities plus challenge/risk-control account transitions into the sink.
  • Added native delivery through Windows tray Toast and Electron notifications on macOS/Linux, configured once during desktop bootstrap.
  • Focused notification/account-pool tests pass (2 files, 8 tests); desktop Vue/TypeScript typecheck passes.
  • Added a direct server regression test for final doubao_challenge_required handling. It verifies same-tenant/date/model/client scoping, current-task exclusion, terminal source statuses, bound arguments, and the authorization-block payload.
  • The focused server test set passes, including existing non-retryable and target-client precedence checks.
  • Full verification passes: desktop 31 files / 212 tests, desktop typecheck, desktop production build, server internal/tenant/app tests, and git diff --check.
  • Final diff review confirmed ordinary recoverable warnings stay in the activity feed, while danger errors and account challenge/risk-control transitions produce native system notifications.
  • Phase 65 is complete. Automated coverage cannot click the host OS notification center, but the Electron native notification path passes typecheck and production bundling.
  • The planning skill completion check reports historical unfinished phases 11/12, 17, and 48/49 from earlier work; current request phases 63-65 are all complete and were not used to rewrite unrelated roadmap state.
  • Files changed for this scope:
    • apps/desktop-client/src/main/runtime-system-notification.ts (created)
    • apps/desktop-client/src/main/runtime-system-notification.test.ts (created)
    • apps/desktop-client/src/main/runtime-controller.ts
    • apps/desktop-client/src/main/bootstrap.ts
    • apps/desktop-client/src/main/tray.ts
    • server/internal/tenant/app/monitoring_callback_service_test.go
    • planning files

Session: 2026-04-30

Phase 37: k3s Deployment Foundation

  • Status: complete
  • Actions taken:
    • Recovered the existing Docker Compose deployment topology and runtime command set.
    • Added a self-contained deploy/k3s kustomize deployment with namespace, config, placeholder secrets, infra StatefulSets, app Deployments/Services, migration/minio-init Jobs, and Traefik Ingress.
    • Included all server-side runtime services: tenant-api, worker-generate, kol-assist-worker, scheduler, ops-api, plus frontend and ops-web.
    • Added Dockerfile.ops-web so the operations frontend can be built as a deployable nginx image.
    • Updated the offline packaging script to build/save ops-api, kol-assist-worker, and ops-web, and to include the k3s directory in generated deploy packages.
    • Updated the offline compose override and .env.example with the missing ops/security knobs.
  • Files created/modified:
    • Dockerfile.ops-web (created)
    • deploy/k3s/ (created)
    • deploy/docker-compose.yaml (updated)
    • deploy/scripts/package.sh (updated)
    • deploy/docker-compose.offline.yaml (updated)
    • deploy/.env.example (updated)
    • task_plan.md / findings.md / progress.md (updated)

Phase 38: k3s Verification

  • Status: complete
  • Verification:
    • kubectl kustomize deploy/k3s rendered 30 Kubernetes objects.
    • Local YAML parsing of the rendered manifest passed.
    • bash -n deploy/scripts/package.sh && bash -n deploy/scripts/load-and-start.sh passed.
    • git diff --check passed.
    • pnpm --filter ops-web build passed.
    • pnpm --filter admin-web typecheck passed.
  • Notes:
    • kubectl apply --dry-run=client could not complete locally because the configured kube-apiserver at 127.0.0.1:26443 is not running; run live API validation on the target k3s node.

Session: 2026-04-20

Phase 20: Tracking Collect-Now Repair

  • Status: complete
  • Actions taken:
    • Re-read the admin-web tracking page, monitoring API client, and tenant monitoring handler/service chain to isolate why 立即采集 still depended on the removed browser-plugin runtime.
    • Confirmed the frontend was still blocking on monitoring-plugin readiness and trying to kickoff a plugin cycle even though monitoring collection is now meant to run silently through desktop-client.
    • Added monitoring dashboard runtime state so the page now knows whether the current logged-in user's own desktop client is online.
    • Tightened collect-now server validation to only accept requests when the current actor has an online desktop client, instead of falling back to another online client in the same workspace.
    • Updated TrackingView.vue to remove the plugin kickoff path, call only the backend collect-now endpoint, and disable the CTA with an explicit reason tooltip when brand/keyword/client-online requirements are not met.
    • Added the missing desktop_client_offline frontend error mapping plus new i18n strings for the button gating reasons.
  • Files created/modified:
    • task_plan.md (updated)
    • findings.md (updated)
    • progress.md (updated)
    • apps/admin-web/src/views/TrackingView.vue (updated)
    • apps/admin-web/src/i18n/messages/zh-CN.ts (updated)
    • apps/admin-web/src/i18n/messages/en-US.ts (updated)
    • apps/admin-web/src/lib/errors.ts (updated)
    • packages/shared-types/src/index.ts (updated)
    • server/internal/tenant/app/monitoring_service.go (updated)

Phase 15: Desktop Scheduler Design & Integration

  • Status: complete
  • Actions taken:
    • Re-read the planning files and recovered the current desktop AI monitoring scope from the existing repo changes.
    • Audited the desktop runtime controller, process metrics sampler, runtime snapshot, session registry, and monitor adapter surface.
    • Confirmed the current desktop task executor is global-serial, monitor-task staleness dropping is already implemented, and no durable local queue/cache exists yet.
    • Confirmed playwright-core is installed but connectOverCDP() is not wired, while hidden Electron execution already exists via BrowserWindow and WebContentsView.
    • Added monitor-scheduler.ts as a durable file-backed local scheduler for monitor tasks, including same-day persistence, stale cross-day pruning, per-platform mutual exclusion, question-level cooldowns, and lease-miss backoff.
    • Refactored runtime-controller.ts so publish tasks keep a conservative explicit queue while monitor tasks are selected through the local scheduler with adaptive max concurrency from Electron process metrics.
    • Extended runtime diagnostics to surface the monitor scheduler snapshot and made the lease manager track multiple active leases instead of a single global lease placeholder.
  • Files created/modified:
    • task_plan.md (updated)
    • findings.md (updated)
    • progress.md (updated)
    • apps/desktop-client/src/main/monitor-scheduler.ts (created)
    • apps/desktop-client/src/main/runtime-controller.ts (updated)
    • apps/desktop-client/src/main/lease-manager.ts (updated)
    • apps/desktop-client/src/main/runtime-snapshot.ts (updated)

Phase 16: Hidden Playwright CDP Execution Layer

  • Status: complete
  • Actions taken:
    • Added playwright-cdp.ts as a reusable hidden-browser manager that launches hidden Electron BrowserWindow instances on account-bound sessions and attaches Playwright via chromium.connectOverCDP().
    • Enabled Electron remote debugging in bootstrap so the app exposes a CDP endpoint for local Playwright attachment.
    • Extended adapter context with an optional Playwright { browser, page } handle and wired the runtime controller to acquire/release hidden Playwright pages when an adapter declares executionMode: "playwright".
    • Kept the existing hidden WebContentsView path intact so current adapters like Doubao continue to work unchanged.
  • Files created/modified:
    • apps/desktop-client/src/main/playwright-cdp.ts (created)
    • apps/desktop-client/src/main/bootstrap.ts (updated)
    • apps/desktop-client/src/main/adapters/base.ts (updated)
    • apps/desktop-client/src/main/runtime-controller.ts (updated)

Phase 17: Verification & Delivery

  • Status: in_progress
  • Actions taken:
    • Added optional scheduler metadata to desktop task events in shared types and server publishers so the local scheduler can make safer fan-out decisions before leasing.
    • Ran gofmt on the modified Go files after the event metadata changes.
    • Verified pnpm --filter @geo/desktop-client typecheck passes.
    • Verified pnpm --filter @geo/desktop-client build passes.
    • Verified pnpm --filter admin-web typecheck still passes after the shared-type event additions.
    • Verified go test ./internal/tenant/app/... passes from server/.
  • Files created/modified:
    • packages/shared-types/src/index.ts (updated)
    • server/internal/tenant/app/desktop_task_events.go (updated)
    • server/internal/tenant/app/desktop_task_service.go (updated)
    • server/internal/tenant/app/publish_job_service.go (updated)

Phase 18: Qwen Adapter & Auth Relaxation

  • Status: complete
  • Actions taken:
    • Ported the browser-extension qwen monitoring flow into a new desktop Playwright adapter that drives Tongyi's in-page managers via page.evaluate() and extracts answer/source data from __qianwenChatAPI.
    • Registered the new qwen monitor adapter in the desktop runtime so monitor tasks can execute through the hidden Playwright CDP layer.
    • Tightened generic AI auth fingerprinting to ignore CSRF-only cookies like XSRF-TOKEN, which Qwen sets even for anonymous visits.
    • Added a Qwen-specific bind detector so the auth window can auto-close once a real session footprint is present, even if the page has not yet rendered an obvious avatar/name signal.
    • Updated monitor preflight policy so only yuanbao, kimi, and deepseek require active login before execution; qwen, doubao, and wenxin are allowed to run anonymously when needed.
  • Files created/modified:
    • task_plan.md (updated)
    • findings.md (updated)
    • progress.md (updated)
    • apps/desktop-client/src/main/adapters/qwen.ts (created)
    • apps/desktop-client/src/main/adapters/index.ts (updated)
    • apps/desktop-client/src/main/runtime-controller.ts (updated)
    • apps/desktop-client/src/main/account-binder.ts (updated)

Phase 19: Qwen Verification & Delivery

  • Status: complete
  • Actions taken:
    • Verified pnpm --filter @geo/desktop-client typecheck passes after the Qwen adapter, bind-flow, and auth-policy updates.
    • Verified pnpm --filter @geo/desktop-client build passes after the same changes.
    • Followed up on a real Qwen bind stall reported by the user, inspected the machine's persisted pending-qwen-* partition, and confirmed logged-in cookies like tongyi_sso_ticket, tongyi_sso_ticket_hash, and b-user-id are present.
    • Reworked Qwen account detection so bind/probe can succeed from those persisted auth cookies even when the page nickname/avatar heuristics do not trigger.
  • Files created/modified:
    • task_plan.md (updated)
    • findings.md (updated)
    • progress.md (updated)

Session: 2026-04-04

Phase 13: Prompt Centralization

  • Status: complete
  • Actions taken:
    • Read the current planning files and reopened repository context for the new prompt-centralization request.
    • Searched runtime and seed paths for all currently used hard-coded prompts instead of only matching the obvious template seed strings.
    • Confirmed the prompt surface spans template seeds, template generation fallback prompts, template assist fallback prompts, prompt-rule generation supplements, and knowledge prompt injection.
    • Added a dedicated server/internal/tenant/prompts package to centralize platform template seed prompts and runtime prompt fragments.
    • Refactored cmd/dev-seed, template_prompt.go, template_assist.go, prompt_generate_service.go, and knowledge_service.go to read prompt text from the new package.
    • Corrected invalid JSON quoting in the seed custom_placeholder fields while moving the template card-config blobs.
  • Files created/modified:
    • task_plan.md (updated)
    • findings.md (updated)
    • progress.md (updated)
    • server/internal/tenant/prompts/runtime.go (created)
    • server/internal/tenant/prompts/template_seeds.go (created)
    • server/cmd/dev-seed/main.go (updated)
    • server/internal/tenant/app/template_prompt.go (updated)
    • server/internal/tenant/app/template_assist.go (updated)
    • server/internal/tenant/app/prompt_generate_service.go (updated)
    • server/internal/tenant/app/knowledge_service.go (updated)

Phase 14: Prompt Verification & Delivery

  • Status: complete
  • Actions taken:
    • Ran gofmt across all changed Go files after the prompt extraction.
    • Re-scanned the runtime and seed callers to confirm the long prompt literals were removed from business functions and now only live under server/internal/tenant/prompts.
    • Ran go test ./internal/tenant/... ./cmd/dev-seed from server/ and confirmed the affected packages compile and tests pass.
  • Files created/modified:
    • task_plan.md (updated)
    • findings.md (updated)
    • progress.md (updated)

Session: 2026-03-31

Phase 10: Interface Coverage Audit & Visual Mapping

  • Status: complete
  • Actions taken:
    • Re-read the backend router and transport/app layers to inventory every currently exposed tenant-facing HTTP interface.
    • Confirmed the remaining frontend-backed API groups are templates/articles and brands, while custom/media/tracking/knowledge still lack matching backend routes in this repo.
    • Recovered the reference screenshot layouts with position-aware OCR so the implementation can follow actual placement and grouping instead of just textual labels.
  • Files created/modified:
    • task_plan.md (updated)
    • findings.md (updated)
    • progress.md (updated)

Phase 11: Admin-Web Full Interface Delivery

  • Status: in_progress
  • Actions taken:
    • Added a minimal vue-i18n setup with zh-CN and en-US message bundles, locale persistence, and Ant Design locale switching.
    • Reworked LoginView, WorkspaceView, TemplatesView, ArticleDetailDrawer, TemplateGenerationDrawer, PageHero, and BrandsView to stop using inline styles and to use component-local <style> blocks.
    • Restored AppShell.vue to the user-preferred version and only moved its inline style attributes into named classes.
    • Fixed the broken pnpm dev:admin startup path by adding the missing BrandsView.vue, running pnpm install, and re-running pnpm typecheck:admin plus pnpm dev:admin.
  • Files created/modified:
    • apps/admin-web/package.json (updated)
    • apps/admin-web/src/i18n/index.ts (created)
    • apps/admin-web/src/i18n/messages/zh-CN.ts (created)
    • apps/admin-web/src/i18n/messages/en-US.ts (created)
    • apps/admin-web/src/main.ts (updated)
    • apps/admin-web/src/App.vue (updated)
    • apps/admin-web/src/router/meta.d.ts (updated)
    • apps/admin-web/src/router/index.ts (updated)
    • apps/admin-web/src/lib/api.ts (updated)
    • apps/admin-web/src/lib/display.ts (updated)
    • apps/admin-web/src/components/PageHero.vue (created)
    • apps/admin-web/src/components/ArticleDetailDrawer.vue (created)
    • apps/admin-web/src/components/TemplateGenerationDrawer.vue (created)
    • apps/admin-web/src/views/LoginView.vue (updated)
    • apps/admin-web/src/views/WorkspaceView.vue (updated)
    • apps/admin-web/src/views/TemplatesView.vue (created)
    • apps/admin-web/src/views/BrandsView.vue (created)
    • apps/admin-web/src/views/FeatureStubView.vue (updated)
    • apps/admin-web/src/layouts/AppShell.vue (updated)
    • packages/shared-types/src/index.ts (updated)
    • apps/admin-web/src/styles.css (pending next cleanup pass)
    • task_plan.md (updated)
    • findings.md (updated)
    • progress.md (updated)

Phase 6: Frontend Scope Recovery

  • Status: complete
  • Actions taken:
    • Read the planning files, Claude-side plan artifact, and architecture documents to recover the interrupted task context.
    • Confirmed the backend chain is complete locally and the repository still has no apps/ or packages/ directories.
    • Locked the next useful slice to a tenant-facing admin-web foundation rather than platform-side frontend work.
  • Files created/modified:
    • task_plan.md (updated)
    • findings.md (updated)
    • progress.md (updated)

Phase 7: Frontend Foundation

  • Status: complete
  • Actions taken:
    • Created the root pnpm workspace, root package scripts, and frontend-oriented .gitignore.
    • Added shared frontend packages for TS config, API types, and an Axios-based client with auth refresh handling.
    • Scaffolded apps/admin-web with Vite 8, Vue 3, TypeScript, Ant Design Vue, Pinia, Vue Router, and Vue Query.
  • Files created/modified:
    • .gitignore (created)
    • package.json (created)
    • pnpm-workspace.yaml (created)
    • packages/tsconfig/base.json (created)
    • packages/shared-types/package.json (created)
    • packages/shared-types/src/index.ts (created)
    • packages/http-client/package.json (created)
    • packages/http-client/src/index.ts (created)
    • apps/admin-web/package.json (created)
    • apps/admin-web/.env.example (created)
    • apps/admin-web/index.html (created)
    • apps/admin-web/tsconfig.json (created)
    • apps/admin-web/vite.config.ts (created)

Phase 8: Admin-Web Initial Features

  • Status: complete
  • Actions taken:
    • Implemented the app bootstrap, Ant Design locale wiring, session persistence helpers, and auth store.
    • Implemented the login page with seeded local credentials and redirect-aware sign-in flow.
    • Implemented the protected shell layout, navigation, quota teaser, workspace dashboard, and placeholder routes for the rest of the information architecture.
  • Files created/modified:
    • apps/admin-web/src/App.vue (created)
    • apps/admin-web/src/main.ts (created)
    • apps/admin-web/src/styles.css (created)
    • apps/admin-web/src/lib/session.ts (created)
    • apps/admin-web/src/lib/errors.ts (created)
    • apps/admin-web/src/lib/api.ts (created)
    • apps/admin-web/src/stores/pinia.ts (created)
    • apps/admin-web/src/stores/auth.ts (created)
    • apps/admin-web/src/router/meta.d.ts (created)
    • apps/admin-web/src/router/index.ts (created)
    • apps/admin-web/src/layouts/AppShell.vue (created)
    • apps/admin-web/src/views/LoginView.vue (created)
    • apps/admin-web/src/views/WorkspaceView.vue (created)
    • apps/admin-web/src/views/FeatureStubView.vue (created)

Phase 9: Frontend Verification & Delivery

  • Status: complete
  • Actions taken:
    • Ran pnpm install for the new workspace and generated pnpm-lock.yaml.
    • Fixed the initial frontend build errors related to Node typings and typed Axios wrapper method parameters.
    • Switched Ant Design Vue registration from full-plugin mode to per-component registration to reduce the main bundle size.
    • Ran pnpm build:admin successfully and confirmed production assets were emitted.
    • Ran pnpm --filter admin-web preview --host 127.0.0.1 --port 4173 and verified /login returned HTTP/1.1 200 OK with built asset references.
    • Attempted browser automation verification, then fell back to HTTP verification after the local Playwright MCP environment failed on /.playwright-mcp.
    • After Playwright became usable again, started the Vite dev server plus tenant-api, discovered the login CTA was not submitting on click, fixed the handler binding, and re-ran browser verification successfully with Playwright CLI.
  • Files created/modified:
    • apps/admin-web/package.json (updated)
    • apps/admin-web/tsconfig.json (updated)
    • apps/admin-web/vite.config.ts (updated)
    • apps/admin-web/src/main.ts (updated)
    • apps/admin-web/src/views/LoginView.vue (updated)
    • packages/http-client/src/index.ts (updated)
    • pnpm-lock.yaml (created)
    • task_plan.md (updated)
    • findings.md (updated)
    • progress.md (updated)

Phase 1: Progress Verification

  • Status: complete
  • Started: 2026-03-31
  • Actions taken:
    • Read the task-specific design document.
    • Inspected top-level repository layout and backend directory tree.
    • Read the planning-with-files skill instructions and initialized planning files.
    • Inspected server/Makefile, server/cmd/tenant-api/main.go, and server/internal/bootstrap/bootstrap.go.
    • Verified there are 19 migration files and inspected cmd/dev-seed/main.go.
    • Inspected sqlc.yaml, repository directory contents, and current repository integration test placeholders.
    • Inspected auth/workspace/template/article/brand services and route registration to compare actual implementation with the design doc.
  • Files created/modified:
    • task_plan.md (created)
    • findings.md (created)
    • progress.md (created)

Phase 2: Gap Analysis and Scope Lock

  • Status: complete
  • Actions taken:
    • Compared the repository contents against the design doc's expected sqlc and repository layout.
    • Confirmed Step 4 is the earliest incomplete milestone because the generated code directory and repository wrappers are missing.
    • Ran make sqlc-generate and observed a failing schema path in sqlc.yaml.
  • Files created/modified:
    • task_plan.md (updated)
    • findings.md (updated)
    • progress.md (updated)

Phase 3: Implementation

  • Status: complete
  • Actions taken:
    • Fixed server/internal/tenant/repository/sqlc.yaml and regenerated sqlc output under server/internal/tenant/repository/generated/.
    • Added repository wrappers for auth, workspace, template, article, quota, and audit queries.
    • Refactored auth/workspace/template services and handlers to use the new repository layer.
    • Reworked template generation flow to use repository-backed transactional operations.
    • Implemented atomic refresh token rotation in Redis and added session_store tests.
    • Cleaned existing lint violations that were blocking local verification.
  • Files created/modified:
    • server/internal/tenant/repository/sqlc.yaml (updated)
    • server/internal/tenant/repository/generated/* (generated)
    • server/internal/tenant/repository/common.go (created)
    • server/internal/tenant/repository/auth_repo.go (created)
    • server/internal/tenant/repository/workspace_repo.go (created)
    • server/internal/tenant/repository/template_repo.go (created)
    • server/internal/tenant/repository/article_repo.go (created)
    • server/internal/tenant/repository/quota_repo.go (created)
    • server/internal/tenant/repository/audit_repo.go (created)
    • server/internal/tenant/app/auth_service.go (updated)
    • server/internal/tenant/app/workspace_service.go (updated)
    • server/internal/tenant/app/template_service.go (updated)
    • server/internal/shared/auth/session_store.go (updated)
    • server/internal/shared/auth/session_store_test.go (created)
    • server/internal/tenant/transport/auth_handler.go (updated)
    • server/internal/tenant/transport/workspace_handler.go (updated)
    • server/internal/tenant/transport/template_handler.go (updated)

Phase 4: Testing & Verification

  • Status: complete
  • Actions taken:
    • Re-ran make sqlc-generate after the final code state and confirmed generation succeeds.
    • Ran go test ./..., then make test, and confirmed all tests pass.
    • Ran make lint, fixed the reported warnings, and re-ran until it passed cleanly.
    • Verified the tenant-scope guard script exists and checked for CI workflow files.
  • Files created/modified:
    • server/internal/tenant/repository/tx.go (updated)
    • server/cmd/dev-seed/main.go (updated)
    • server/internal/shared/auth/middleware_test.go (updated)
    • server/internal/tenant/app/article_service.go (updated)
    • server/internal/tenant/app/brand_service.go (updated)
    • task_plan.md (updated)
    • findings.md (updated)
    • progress.md (updated)

Phase 5: Delivery

  • Status: complete
  • Actions taken:
    • Added backend CI workflow under .github/workflows/.
    • Added tenant-scope-guard to the backend Makefile so CI and local verification use the same entrypoint.
    • Re-ran make tenant-scope-guard, make test, and make lint after the CI-related edits.
    • Updated the backend Makefile to run golang-migrate through go run -tags postgres instead of requiring a preinstalled migrate binary.
    • Ran make dev-init successfully, including Docker startup, migrations, and seed data.
    • Started tenant-api and verified health endpoints plus auth login/me/refresh/logout behavior with positive and negative checks.
  • Files created/modified:
    • .github/workflows/backend-ci.yml (created)
    • server/Makefile (updated)
    • task_plan.md (updated)
    • findings.md (updated)
    • progress.md (updated)

Test Results

Test Input Expected Actual Status
Repository inspection ls, rg --files, sed Establish current backend coverage Backend skeleton, migrations, services, and transport files exist pass
Migration count `find server/migrations -name '*.up.sql' wc -l` 19 schema migrations 19
SQLC generation make sqlc-generate Generate typed code Passed after fixing sqlc.yaml schema path pass
Go tests go test ./... All packages compile and tests pass Passed pass
Make tests make test Roadmap acceptance command passes Passed pass
Lint make lint Roadmap acceptance command passes Passed after fixing errcheck/ineffassign warnings pass
CI workflow presence find . -path '*/.github/workflows/*' -type f At least one CI workflow if Step 9 is complete .github/workflows/backend-ci.yml exists pass
Tenant scope guard make tenant-scope-guard Tenant SQL filter guard passes Passed pass
Dev init make dev-init Infra, migrations, and seed complete successfully Passed after fixing Makefile-managed migrate driver tags pass
Live health GET /api/health/live HTTP 200 with alive status Returned code=0, status=alive pass
Ready health GET /api/health/ready HTTP 200 with ready status Returned code=0, status=ready pass
Login POST /api/auth/login with admin@geo.local / Admin@123 Receive access/refresh tokens and user info Passed pass
Me GET /api/auth/me with fresh access token Current user payload returned Passed pass
Refresh POST /api/auth/refresh with original refresh token Receive rotated access/refresh token pair Passed pass
Logout revoke POST /api/auth/logout, then GET /api/auth/me with old access token Old access token rejected Returned 40103 token_revoked pass
Refresh rotate invalidate Reuse old refresh token after refresh Old refresh token rejected Returned 40121 refresh_session_expired pass
Workspace overview GET /api/tenant/workspace/overview Aggregated stats return successfully Returned code=0, article/brand counts present pass
Workspace recent articles GET /api/tenant/workspace/recent-articles Recent list returns successfully Returned code=0 pass
Workspace quota summary GET /api/tenant/workspace/quota-summary Quota summary returns successfully Returned code=0, balance/total present pass
Workspace template cards GET /api/tenant/workspace/template-cards Template cards return successfully Returned code=0, count=4 pass
Template generate flow POST /api/tenant/templates/{id}/generate + article detail/version checks Article generation completes end-to-end Article 1 completed, versions count 1, quota 100 -> 99 pass
Brand soft delete and recreate Create brand, delete, recreate same name Recreate succeeds after soft delete Original brand 1 deleted, recreated as brand 2 pass
Question version history Create question, update, list versions Version history retained Returned 2 versions, latest text updated pass
Competitor CRUD basic flow Create competitor then list Competitor visible under brand Returned count 1 pass
Frontend workspace install pnpm install Workspace dependencies install cleanly Passed pass
Frontend build pnpm build:admin admin-web compiles and emits production assets Passed pass
Frontend preview login HTML curl -I http://127.0.0.1:4173/login Preview route returns 200 OK Returned HTTP/1.1 200 OK pass
Frontend preview asset shell curl -s http://127.0.0.1:4173/login HTML references built CSS/JS assets Returned built asset tags for index-CtRpygQP.js and index-DFz5-Rsr.css pass
Frontend browser login flow pnpm dlx @playwright/test test .tmp-playwright/admin-web.spec.js --reporter=line Login succeeds and workspace renders Passed after binding the login CTA directly to handleSubmit pass

Error Log

Timestamp Error Attempt Resolution
2026-03-31 None yet 1 N/A
2026-03-31 make sqlc-generate failed due to invalid schema path in sqlc.yaml 1 Fixed by changing schema path to ../../../migrations/
2026-03-31 make lint failed on several existing warnings 1 Fixed all reported warnings and re-ran lint successfully
2026-03-31 make dev-init failed because go-run migrate lacked the postgres driver tag 1 Updated MIGRATE_CMD to go run -tags postgres ... and reran successfully
2026-03-31 Gemini CLI returned 429 MODEL_CAPACITY_EXHAUSTED on gemini-3.1-pro-preview 1 Stopped pursuing Gemini once the user said not to use Gemini
2026-03-31 Initial frontend build failed on missing Node typings and implicit any parameters 1 Removed the Node type dependency from app TS config, simplified the Vite config, and typed the API client methods explicitly
2026-03-31 Playwright MCP browser open failed on /.playwright-mcp 1 Used preview HTTP verification instead of blocking on browser environment setup
2026-03-31 Browser click on the login CTA did not submit the form, so Playwright stayed on /login 1 Added an explicit @click=\"handleSubmit\" binding to the CTA and re-ran the E2E flow successfully

Phase 22: Monitoring Daily Collection Review

  • Status: complete
  • Actions taken:
    • Read the existing planning files and restored context for the monitoring scheduler/runtime work.
    • Inspected cmd/scheduler, the shared cron helper, ScheduleDispatchWorker, monitoring handlers, monitoring service, phase2 desktop task dispatch, migrations, and deployment config.
    • Queried local Postgres/Monitoring Postgres containers for current task evidence.
    • Verified the data-detail/dashboard read path with existing 2026-04-24 monitoring data.
  • Findings so far:
    • Current DB evidence shows 2026-04-24 monitoring collection tasks are desktop/manual/plugin_standard, not daily automatic tasks.
    • tenant_monitoring_quotas is configured as collect_frequency = daily, but the inspected runtime does not contain a daily monitoring task generation worker.

Phase 23: Monitoring Daily Task Generation Repair

  • Status: complete
  • Actions taken:
    • Added server/internal/tenant/app/monitoring_daily_task_worker.go.
    • Wired MonitoringDailyTaskWorker into server/cmd/scheduler/main.go.
    • Added server/internal/tenant/app/monitoring_daily_task_worker_test.go for midnight-after materialization, existing-task idempotency, and deterministic plan ordering.
    • Implemented stable jitter after midnight, global/plan/brand batch caps, daily hard-cap selection, and brand-level advisory locking.
    • Implemented due desktop dispatch claim with FOR UPDATE SKIP LOCKED, last_dispatched_at, dispatch_attempts, and a retry cooldown.
    • Kept automatic daily desktop tasks pending when no eligible desktop account/client is available; no legacy fallback is applied for this path.
    • Fixed the P0 rollout-disabled case: daily generation now requires desktop_tasks rollout plus primary_client_id; otherwise it skips the plan and records SkippedPlanCount rather than creating orphan automatic/legacy rows.
    • Removed Go runtime and dev-seed dependency on tenant_monitoring_quotas: daily plans now come from desktop_clients, dashboard/projection use default monitoring platforms, and primary client selection is derived from recent/registered desktop clients.
    • Fixed the P0 RabbitMQ publish context issue in daily worker: publishMonitorDesktopTaskAvailable now receives a 5-second child context derived from the worker tick context instead of context.Background().
  • Verification:
    • go test ./internal/tenant/app -run 'Test.*MonitoringDaily|Test.*Monitoring|TestEncodeMonitoringProjectionRebuildEnvelope|TestBuildMonitoringRawPayload' -count=1 passed.
    • go test ./cmd/scheduler ./cmd/dev-seed ./internal/tenant/app -count=1 passed.
    • docker exec geo-monitoring-postgres psql ... EXPLAIN ... passed for the new claim SQL.

Phase 24: Monitoring Daily Observability Hardening

  • Status: complete
  • Actions taken:
    • Completed the plan/brand scoped error-isolation path so transient failures increment summary failure counts and do not stop other tenants/brands in the same tick.
    • Added monitoring_daily_task_metrics.go with atomic counters/gauges for worker runs, fatal and partial failures, plan/brand failures, skipped plans, task stage counts, last timestamps, duration, business date, and last fatal error code/message.
    • Recorded metrics on every runOnce result, including zero-work ticks and fatal errors.
    • Exposed /metrics in Prometheus text format and /api/internal/metrics/monitoring/daily-tasks as JSON.
    • Started the scheduler's health/metrics HTTP server on scheduler.http_port default 8081.
    • Reworked daily hard-cap accounting so existing materialized tasks are counted from DB, full candidate horizons are preserved for de-duplication, and remaining capacity is decremented only by rows inserted in the current tick.
    • Split daily materialization limit from desktop dispatch limit, so a full daily cap does not stop already-pending tasks from being dispatched.
    • Replaced the hard-coded daily plan defaults with subscription/config-driven rules: active subscription required, max brands from plan/config, platforms from live bound desktop accounts, and per-brand task cap from configured active questions capped by brand_library.
    • Changed dashboard and projection platform selection so unbound platforms are not silently shown or scheduled.
  • Verification:
    • go test ./internal/tenant/app -run 'Test.*MonitoringDaily' -count=1 passed.
    • go test ./internal/tenant/app -run 'Test.*MonitoringDaily|TestReconcile|TestResolve|TestIntersect|TestFilter' -count=1 passed.
    • go test ./cmd/scheduler ./internal/bootstrap ./internal/shared/config -count=1 passed.
    • go test ./cmd/scheduler ./cmd/dev-seed ./internal/tenant/app -count=1 passed.
    • go test ./... passed from server/.
    • git diff --check passed.

Phase 25: Heartbeat Primary Stability Hardening

  • Status: complete
  • Actions taken:
    • Replaced heartbeat primary resolution with a per-tenant/per-workspace desktop_client_primary_leases lease row.
    • The current primary renews the lease on heartbeat; other desktop clients are non-primary while the incumbent lease is live.
    • Failover now happens only when the lease has expired or the recorded primary desktop client is unavailable/revoked.
    • Updated daily monitoring plan selection to prefer the sticky lease primary before deterministic fallback.
    • Removed last_seen_at ordering from the current-user registered-client lookup so UI platform selection is not heartbeat-driven.
    • Added migration 20260424130000_add_desktop_primary_leases with the lease table, backfill, and active desktop-client lookup indexes.
    • Added a unit test for the lease-claim decision matrix.
  • Verification:
    • go test ./internal/tenant/app -run 'TestShouldClaimHeartbeatPrimaryLease|Test.*MonitoringDaily|TestReconcile|TestResolve|TestIntersect|TestFilter' -count=1 passed.
    • go test ./... passed from server/.
    • git diff --check passed.

Phase 26: Scheduler Metrics HTTP Hardening

  • Status: complete
  • Actions taken:
    • Added scheduler.http_host with default 127.0.0.1.
    • Added scheduler.internal_metrics_token, with env overrides SCHEDULER_INTERNAL_METRICS_TOKEN / SCHEDULER_METRICS_TOKEN.
    • Protected /metrics and /api/internal/metrics/monitoring/daily-tasks with token auth.
    • Changed scheduler.http_port < 0 to mean explicit HTTP disablement; 0 still defaults to 8081.
    • Replaced app.Engine.Run(... Fatalf ...) with http.Server.ListenAndServe, non-fatal error logging, and graceful Shutdown.
    • Added scheduler HTTP auth/address tests and config normalization/env tests.
  • Verification:
    • go test ./cmd/scheduler ./internal/shared/config -count=1 passed.
    • go test ./... passed from server/.
    • git diff --check passed.

Phase 27: Scheduler Worker Graceful Shutdown

  • Status: complete
  • Actions taken:
    • Added blocking Run(ctx) methods to scheduler workers while keeping existing Start(ctx) wrappers.
    • Updated cmd/scheduler to create workers explicitly and run them under a sync.WaitGroup.
    • On signal, scheduler now stops the metrics HTTP server, cancels worker loops, and waits up to 60 seconds for workers to finish.
    • Changed scheduler worker loops so shutdown cancellation prevents the next tick but does not immediately cancel an active runOnce; active work still has the existing per-worker timeout.
    • Added tests for worker wait completion and timeout behavior.
  • Verification:
    • go test ./cmd/scheduler ./internal/scheduler ./internal/tenant/app -run 'TestScheduler|TestShouldClaimHeartbeatPrimaryLease|Test.*MonitoringDaily' -count=1 passed.
    • go test ./... passed from server/.
    • git diff --check passed.

Phase 28: Monitoring Platform Authorization UI Contract

  • Status: complete
  • Actions taken:
    • Added explicit monitoring platform authorization status to dashboard runtime and question-detail API responses.
    • Added shared frontend type MonitoringPlatformAuthorizationStatus.
    • Updated TrackingView to keep dashboard platform rows stable and disable collect-now for no-authorized or selected-unauthorized platform states without rendering a no-authorized dashboard alert.
    • Updated TrackingQuestionDetailView to show an explicit unavailable state and suppress the platform tabs/detail grid when no platforms are authorized.
    • Added i18n strings for the new no-desktop, no-authorized, and selected-unauthorized states.
    • Added a Go unit test for the authorization-status decision matrix.
  • Verification:
    • go test ./internal/tenant/app -run 'TestMonitoringPlatformAuthorizationStatus|TestReconcileEnabledMonitoringPlatforms|TestFilterMonitoringPlatforms|TestResolveDashboardDateWindowAt|TestParseDetailDateRangeAt' -count=1 passed.
    • pnpm typecheck:admin passed.
    • Browser regression against admin-web with platform_authorization_status = no_authorized_platforms passed for dashboard and question detail; dashboard does not show a no-authorized alert.
    • go test ./... passed from server/.
    • pnpm build:admin passed; it still reports existing bundle/CSS warnings unrelated to this change.
    • git diff --check passed.

Phase 29: Monitoring Six-Platform Display Contract

  • Status: complete
  • Actions taken:
    • Changed loadMonitoringProjectionPlatforms to return the canonical six-platform catalog instead of deriving display rows from same-day task rows.
    • Changed dashboard platform breakdown to use the same six-platform display catalog, so platforms with zero tasks/samples still appear.
    • Added authorized_monitoring_platform_ids to dashboard runtime and shared frontend types.
    • Updated TrackingView collect-now gating to use the explicit authorized platform ID list instead of inferring authorization from visible platform rows.
    • Added unit coverage for the six-platform catalog copy and projection platform selection.
  • Verification:
    • go test ./internal/tenant/app -run 'TestDefaultMonitoringPlatformMetadata|TestLoadMonitoringProjectionPlatforms|TestMonitoringPlatformAuthorizationStatus|TestReconcileEnabledMonitoringPlatforms|TestFilterMonitoringPlatforms' -count=1 passed.
    • pnpm typecheck:admin passed.
    • Browser regression confirmed a no-authorized-platform dashboard still renders all 6 platform names and keeps collect-now disabled.
    • go test ./... passed from server/.
    • pnpm build:admin passed; existing CSS/chunk warnings remain unrelated.
    • git diff --check passed.

Phase 30: Workspace Scope And Daily Config Visibility

  • Status: complete
  • Actions taken:
    • Added current-workspace context helpers and a WorkspaceScope middleware with X-Workspace-ID support plus membership validation for non-primary workspace requests.
    • Wired the tenant route group through WorkspaceScope.
    • Updated monitoring dashboard, question detail, and collect-now paths to use the current request workspace for quota/client/platform-account/access-state resolution.
    • Documented findLatestRegisteredClientForUser as created-at ordered and intentionally independent of online health.
    • Changed daily worker platform JSON decoding to return errors instead of swallowing malformed JSON.
    • Counted malformed daily platform config as a plan failure and logged it with tenant/workspace/business-date context.
    • Added unit coverage for current workspace fallback/override, workspace-id parsing, and daily platform JSON decode failures.
  • Verification:
    • go test ./internal/shared/auth ./internal/shared/middleware ./internal/tenant/app -run 'TestCurrentWorkspaceID|TestWorkspaceScope|TestRequestedWorkspaceID|TestDecodeDailyMonitoringEnabledPlatforms|Test.*MonitoringDaily|TestMonitoringPlatformAuthorizationStatus|TestDefaultMonitoringPlatformMetadata|TestLoadMonitoringProjectionPlatforms' -count=1 passed.
    • go test ./internal/tenant/transport -run TestProtectedRoutes -count=1 passed.
    • go test ./... passed from server/.
    • git diff --check passed.

Phase 31: Heartbeat Primary Lease Scalability

  • Status: complete
  • Actions taken:
    • Replaced per-heartbeat BEGIN / SELECT ... FOR UPDATE / UPDATE primary lease handling with read-mostly lease resolution.
    • Normal primary heartbeats now return from a plain read unless the renew interval has elapsed.
    • Non-primary heartbeats return from a plain read while the incumbent lease is live and available.
    • Missing, expired, unavailable, and race cases use single-statement INSERT or CAS-style UPDATE writes instead of an explicit lock transaction.
    • Added heartbeat primary lease metrics for hit/miss outcomes, DB writes, contention, errors, and duration.
    • Added token-protected tenant-api internal metrics endpoints for heartbeat primary lease JSON and Prometheus output.
    • Documented the renew throttle relative to the 25s desktop heartbeat and 90s TTL.
  • Verification:
    • go test ./internal/tenant/app -run 'TestDecideHeartbeatPrimaryLease|TestMonitoringHeartbeatPrimaryLeaseMetrics|TestBuildMonitoringRawPayloadKimi' -count=1 passed.
    • go test ./internal/tenant/transport -run 'TestProtectedRoutes|TestPublicRoutes' -count=1 passed.
    • go test ./... passed from server/.
    • git diff --check passed.

Phase 32: Monitoring Heartbeat Snapshot Scalability

  • Status: complete
  • Actions taken:
    • Changed MonitoringCallbackService.Heartbeat to batch-load current access snapshot state before deciding whether a monitoring DB transaction is required.
    • Stable unchanged platform access reports now skip snapshot upserts; unchanged rows refresh at most every 10 minutes.
    • Primary-client inaccessible-platform reconciliation now first checks whether matching pending tasks exist, so stable inaccessible reports with no pending work do not open a write transaction.
    • Added heartbeat snapshot metrics for platform reports, transaction opens, snapshot insert/update/refresh/skip counts, reconciled tasks, projection rebuilds, errors, and duration.
    • Exposed heartbeat snapshot metrics through token-protected tenant-api internal JSON and Prometheus endpoints.
    • Added a monitoring DB partial index for pending-platform reconciliation lookup.
  • Verification:
    • go test ./internal/tenant/app -run 'TestDecideMonitoringHeartbeatSnapshot|TestMonitoringHeartbeatSnapshotMetrics|TestDecideHeartbeatPrimaryLease|TestMonitoringHeartbeatPrimaryLeaseMetrics' -count=1 passed.
    • go test ./internal/tenant/app -count=1 passed.
    • go test ./internal/tenant/transport -count=1 passed.
    • go test ./... passed from server/.
    • git diff --check passed.

Phase 33: Prometheus Metrics Exposition Hardening

  • Status: complete
  • Actions taken:
    • Added prometheus/client_golang and replaced custom hand-written Prometheus text builders with prometheus.Collector implementations plus promhttp.
    • Kept existing JSON snapshot endpoints unchanged.
    • Changed scheduler /metrics to serve a dedicated registry containing daily-task metrics plus Go runtime and process collectors.
    • Changed tenant-api internal heartbeat Prometheus endpoints to serve through promhttp as well.
    • Removed raw error-message labels from heartbeat snapshot Prometheus output; detailed messages remain available in JSON snapshots.
    • Added explicit carriage-return normalization for label values before creating const metrics, because the local client_golang text path does not escape raw CR.
    • Added tests covering scheduler runtime metrics and CR label normalization.
  • Verification:
    • go test ./internal/tenant/app -run 'TestMonitoringDailyTaskMetrics|TestMonitoringDailyTaskPrometheus|TestMonitoringSchedulerMetricsPrometheusHandler|TestMonitoringHeartbeatPrimaryLeaseMetrics|TestMonitoringHeartbeatSnapshotMetrics' -count=1 passed.
    • go test ./cmd/scheduler ./internal/tenant/transport -run 'TestScheduler|TestProtectedRoutes|TestPublicRoutes|TestDesktopClientRoutes' -count=1 passed.
    • go test ./... passed from server/.
    • git diff --check passed.

Phase 34: Daily Task Batch Materialization

  • Status: complete
  • Actions taken:
    • Changed ensureDailyMonitoringCollectTasks from per-candidate tx.Exec to a single batched INSERT ... SELECT FROM unnest(...).
    • Batched arrays carry question_id, question_hash, ai_platform_id, and dispatch_after; tenant/brand/business date/collector/run mode/target client/shard key/execution owner remain constant per call.
    • Kept ON CONFLICT DO NOTHING, so duplicate materialization still returns only the number of newly inserted rows via RowsAffected.
  • Verification:
    • go test ./internal/tenant/app -run 'Test.*MonitoringDaily|TestMonitoringDailyTaskMetrics|TestMonitoringDailyTaskPrometheus|TestMonitoringSchedulerMetricsPrometheusHandler' -count=1 passed.
    • go test ./... passed from server/.
    • git diff --check passed.

Phase 35: Daily Plan SQL Decomposition

  • Status: complete
  • Actions taken:
    • Split loadDailyMonitoringPlans into loadDailyMonitoringPrimaryClientPlans, loadDailyMonitoringPlanPlatformJSON, and a pure assembly helper.
    • Kept the first query focused on active subscription admission, lease-preferred primary client selection, workspace scoping, and subscription/config-derived brand/question limits.
    • Moved authorized platform aggregation to a second batched query keyed by (tenant_id, workspace_id, client_id).
    • Added a helper test covering the race where a selected client loses its platform snapshot between the two reads; that candidate is skipped instead of becoming an empty executable plan.
  • Verification:
    • go test ./internal/tenant/app -run 'TestBuildDailyMonitoringPlansFromSelectedClients|Test.*MonitoringDaily' -count=1 passed.
    • go test ./internal/tenant/app -run 'Test.*MonitoringDaily|TestMonitoringDailyTaskMetrics|TestMonitoringDailyTaskPrometheus|TestMonitoringSchedulerMetricsPrometheusHandler' -count=1 passed.
    • go test ./... passed from server/.
    • git diff --check passed.

Phase 36: Monitoring Authorization And Detail Display Bug Fix

  • Status: complete
  • Actions taken:
    • Changed monitoring platform authorization to mean a bound, non-deleted platform account on a supported AI platform; platform_accounts.health no longer has to be live.
    • Removed health = 'live' from daily scheduled plan eligibility and platform aggregation, so daily automatic collection can materialize tasks for offline clients and non-live account states.
    • Kept immediate collection gated by desktop-client online state, and made the online check target the same desktop client that owns the authorized platform accounts.
    • Changed question detail to display the canonical platform history instead of hiding existing data behind current platform_authorization_status.
    • Updated frontend date validation to use the monitoring business date, so today's date such as 2026-04-24 is not rejected by local/browser timezone drift.
  • Verification:
    • go test ./internal/tenant/app -run 'Test.*MonitoringDaily|TestMonitoringPlatformAuthorizationStatus|TestReconcileEnabledMonitoringPlatforms|TestFilterMonitoringPlatforms|TestDefaultMonitoringPlatformMetadata|TestLoadMonitoringProjectionPlatforms' -count=1 passed.
    • pnpm --filter admin-web typecheck passed.
    • go test ./... passed from server/.
    • pnpm --filter admin-web build passed.
    • git diff --check passed.

Phase 21: Foreground Publish Admission

  • Status: complete
  • Actions taken:
    • Confirmed the current runtime only starts publish when state.activeExecutions.size === 0, so an already-running monitor can block media publish.
    • Locked the implementation model: publish is foreground, monitor is background, publish receives reserved runtime capacity, and publish platform policy stays in a lightweight in-memory scheduler.
    • Added apps/desktop-client/src/main/publish-scheduler.ts with in-memory queueing, same-platform active locks, and 3-6 second platform cooldowns.
    • Refactored runtime-controller.ts so publish can start while monitor is running, monitor pauses behind any publish backlog, and lease-in-flight is tracked separately for publish vs. monitor.
    • Added hardware/runtime adaptive total concurrency with GEO_DESKTOP_MAX_TOTAL_CONCURRENCY as an optional cap and a reserved publish slot for monitor budgeting.
    • Exposed publishScheduler in the desktop runtime snapshot and added scheduler unit tests.
  • Verification:
    • pnpm --filter @geo/desktop-client build passed.
    • pnpm --filter @geo/desktop-client test passed: 5 files, 26 tests.
    • git diff --check passed.
    • pnpm --filter @geo/desktop-client typecheck failed before checking runtime changes because electron.vite.config.ts has a Vite 5/8 plugin type mismatch.

2026-04-28 Dongchedi Publish Debug

  • Status: in_progress
  • Actions taken:
    • Queried local geo-postgres through docker exec because psql is not installed on the host.
    • Verified article 95 reconstructs to normal markdown through repository.LoadArticleVersionContent; removed the temporary inspection test after use.
    • Updated apps/desktop-client/src/main/adapters/dongchedi.ts so body images first try Dongchedi spice/image; local/relative images use ImageX upload and then run through spice/image before replacement.
    • Added redacted Dongchedi publish rejection diagnostics to the adapter error payload/logs.
    • Added apps/desktop-client/src/main/adapters/dongchedi-content.ts and a unit test to downgrade table HTML to paragraph rows before Dongchedi submit.
    • Followed up after another server exception retry where diagnostics showed no table tags, no local asset URL, and one body image.
    • Removed &nbsp; entities from Dongchedi table-downgraded body HTML.
    • Changed body image handling so ImageX-uploaded local images try Dongchedi spice/image through both session and page-context requests before replacement.
    • After the platform returned dongchedi_image_upload_failed:content_images_missing_1_of_1, tried an ImageX direct URL fallback; the next retry still returned server exception with content_image_upload_strategies = ['imagex_direct_fallback'].
    • Removed the direct ImageX正文图 fallback. Local正文图片 that cannot be converted to a Dongchedi spice URL are now removed from content instead of failing early or submitting a rejected ImageX URL.
    • After a no-body-image retry still returned server exception, changed extra.content_word_cnt from the reference extension's hardcoded 267 to the actual text count of the final submitted HTML.
    • Added content_word_cnt and computed_text_count to Dongchedi rejection diagnostics.
    • Expanded rejection diagnostics with body image host summaries, body image upload strategies, tag/entity counts, content end excerpt, and cover upload dimensions/hosts.
  • Verification:
    • pnpm --filter @geo/desktop-client typecheck passed.
    • pnpm --filter @geo/desktop-client test -- dongchedi passed.
    • pnpm --filter @geo/desktop-client test passed.
    • pnpm --filter @geo/desktop-client build passed and refreshed out/main/bootstrap.cjs.
    • git diff --check passed.

5-Question Reboot Check

Question Answer
Where am I? Phase 9 complete
Where am I going? Next useful slice is turning the placeholder routes into real pages, starting with template creation and brand management
What's the goal? Resume the interrupted task by adding the missing admin-web frontend foundation on top of the finished backend
What have I learned? The tenant backend is stable enough to support a real login + dashboard frontend slice, and a real browser pass was valuable because it caught a submit wiring bug that static checks missed
What have I done? Added the pnpm frontend workspace, built apps/admin-web, wired auth and dashboard data to tenant-api, reduced the bundle size, and passed install/build/preview plus browser login verification

2026-05-01T06:43:58Z - Platform template seed deployment

  • Investigated the "普通通用模版" cards shown in the screenshot.
  • Confirmed the four platform templates are stored in article_templates and currently seeded by server/cmd/dev-seed/main.go.
  • Confirmed production deployment jobs only run database migrations, so a fresh environment can miss the four template records unless dev seed is run manually.
  • Decision: add a production-safe platform-template initializer and wire it into the migrate job/image instead of running the broad dev seed path.

2026-05-01T07:15:00Z - Gitea Registry CI/CD cleanup

  • Kept deploy-config-ci.yml validation-only after the user clarified it should not own images.
  • Scoped frontend-ci.yml to admin-web and ops-web plus shared packages/Dockerfiles; removed browser-extension typecheck from this workflow.
  • Added Gitea Registry publishing to frontend/backend CI with commit8 tags and skip-if-tag-exists behavior through deploy/scripts/gitea-registry-images.sh.
  • Added commit8 image verification to compose/k3s/offline deployment paths so CD fails if CI has not produced the required image.
  • Changed offline package defaults to use commit8 instead of tag names and to pull business images from the Gitea Registry.
  • Verification passed: workflow YAML parse, deploy script bash -n, and sh -n over all workflow run blocks.

2026-05-03T03:03:09Z - Stable PgBouncer Deployment

  • Confirmed the existing repository topology uses application-side pgxpool and direct Postgres service names; no PgBouncer service/config existed before this change.
  • Started Phase 40 to add stable PgBouncer deployment with session pooling, app runtime traffic through PgBouncer, and migration jobs remaining direct to Postgres.
  • Added pgbouncer and monitoring-pgbouncer to production Docker Compose and k3s using edoburu/pgbouncer:v1.24.1-p1, POOL_MODE=session, and AUTH_TYPE=scram-sha-256.
  • Changed runtime app configs to target PgBouncer while keeping Docker/k3s migration jobs direct to postgres / monitoring-postgres.
  • Lowered application-side Postgres pools: tenant runtime uses 12 main / 8 monitoring connections per process; ops runtime uses 5 / 5.
  • Made deployment passwords flow from POSTGRES_PASSWORD / OPS_POSTGRES_PASSWORD placeholders and changed DatabaseConfig.DSN() to URL-escape credentials for stronger production passwords.
  • Included PgBouncer in offline packaging and docs.
  • Verification passed:
    • docker compose -f deploy/docker-compose.yaml --env-file deploy/.env.example config
    • kubectl kustomize deploy/k3s plus local YAML parse
    • bash -n deploy/scripts/package.sh
    • git diff --check
    • go test ./internal/shared/config ./internal/shared/repository/postgres ./internal/ops/config
    • Temporary PgBouncer container attached to existing server_default network returned SELECT 1 through PgBouncer, then was removed.

2026-05-04T20:51:36Z - Compliance Detection Implementation

  • Started Phase 41 for the Revision 8 content compliance detection design.
  • Re-read the design document sections covering data model, API contracts, publish gate ordering, manual-review lifecycle, ops pages, and test requirements.
  • Confirmed the current code has no compliance module yet and that publish job creation currently bypasses content compliance entirely.
  • Added Phase 41-43 to task_plan.md and recorded the initial backend/frontend integration findings.

2026-05-05T09:30:00+08:00 - Compliance backend integration

  • Added tenant compliance HTTP APIs for runtime status, plain/article check, latest check, record-level ack, manual-review submit/latest/cancel, and tenant whitelist/blacklist terms.
  • Wired PublishJobService.Create() through GateForPublish, including effective article version resolution, target platform aggregation, block/needs_ack errors with serialized check details, and transactional ack consumption.
  • Exposed ArticleDetailResponse.current_version_id and extended desktop publish job creation with article/version compliance metadata.
  • Added ops compliance service/handlers/routes for dictionaries, terms, policy, master switch, manual reviews, records, and stats.
  • Verification passed: go test ./internal/tenant/compliance ./internal/tenant/transport ./internal/tenant/app ./internal/tenant/repository ./internal/ops/app ./internal/ops/transport ./cmd/ops-api ./cmd/worker-generate.

2026-05-05T10:45:00+08:00 - Compliance completion recovery

  • Continued after the 429 interruption and completed the remaining compliance display/API gap.
  • Added ops-web shared type aliases, content-safety navigation, and pages for policy, dictionaries, dictionary editing, manual reviews, manual-review detail, records, and stats.
  • Added admin-web compliance publish/editing integration, including editor checks, violation grouping, locate/highlight support, whitelist creation, publish gate handling, acknowledgement flow, and manual-review submission/cancel paths.
  • Added LLM review AI-point reservation/completion/refund handling in tenant compliance jobs and wired the review worker to the configured LLM provider.
  • Extended desktop publish task listing with publish-job compliance block metadata and updated desktop Publish Management to show 合规阻断, record id, intercepted time, and a readable reason summary.
  • Added admin-web publish status mapping/i18n for blocked_by_compliance.
  • Verification passed:
    • go test ./internal/tenant/compliance ./internal/tenant/transport ./internal/tenant/app ./internal/worker/generate ./cmd/worker-generate
    • go test ./...
    • pnpm --filter admin-web typecheck
    • pnpm --filter ops-web typecheck
    • pnpm --filter @geo/desktop-client typecheck
    • pnpm --filter admin-web build
    • pnpm --filter ops-web build
    • pnpm --filter @geo/desktop-client build
    • git diff --check
  • Notes:
    • Admin-web and ops-web production builds still warn about large chunks from existing Ant Design / Milkdown bundles.

2026-05-05T20:09:10+08:00 - Remove tenant compliance policy overrides

  • Removed the ops-web tenant override route, menu item, API helpers, and ComplianceTenantOverridesView.vue.
  • Removed the ops-api tenant policy override routes, handlers, service methods, audit action, and tenant override resolver branch.
  • Changed ops.compliance_policies to an id=1 global singleton with no scope / tenant_id columns; seed migrations and tenant-api policy loading now read/update that singleton directly.
  • Updated the current compliance design doc, the archived v1 design doc, and planning notes so tenant policy overrides are no longer documented as a supported feature.
  • Verification passed:
    • pnpm --filter ops-web typecheck
    • go test ./internal/ops/app ./internal/ops/transport ./internal/tenant/compliance ./cmd/ops-api

2026-05-05T21:20:00+08:00 - Article generation reliability hardening

  • Started Phase 44 after the user requested large-company style safeguards: structured logs, verifiable states, no swallowed errors, and automatic alerts.
  • Confirmed the remaining weak points are in article body generation worker paths, not title/outline assist paths:
    • Initial task running updates are ignored in template/custom/imitation generation execution.
    • Failure cleanup logs status update errors but still swallows quota ledger/refund errors.
    • Recovery requeue/finalization logs do not carry a consistent event/stage/anomaly schema yet.
  • Scope locked to generation worker/app observability and state checks, avoiding unrelated deployment/compliance dirty files.

2026-05-05T23:45:00+08:00 - Article generation reliability verification

  • Added structured article-generation task log context and counters for transitions, failures, cleanup failures, queue failures, recovery actions, state anomalies, and state-check failures.
  • Added bounded scheduler-owned generation state consistency checks with configurable interval/timeout/batch/lookback and partial DB indexes; this is a low-frequency safety net, not a full-table polling loop.
  • Added internal JSON/Prometheus metrics endpoints and Prometheus alert rules for cleanup failures, state anomalies, state-check failures, queue failures, failure spikes, and recovery finalization.
  • Fixed compile/test follow-ups: matched the state-check DB interface to pgx.Rows and made the Prometheus label assertion order-independent.
  • Verification passed:
    • cd server && go test ./internal/shared/config ./internal/tenant/app ./internal/tenant/transport ./internal/worker/generate ./cmd/scheduler ./cmd/worker-generate
    • cd server && go test ./...
    • git diff --check
    • ruby -e 'require "yaml"; YAML.load_file("deploy/monitoring/article-generation-alerts.yml"); puts "ok"'

2026-05-06T13:05:00+08:00 - Desktop client account isolation

  • Fixed the desktop media account cross-device leak reported from Mac/Windows login:
    • /api/desktop/accounts now lists accounts by the authenticated desktop client_id, not workspace_id + user_id.
    • Desktop account identity lookup and upsert now use (workspace_id, client_id, platform_id, platform_uid), preventing one machine from overwriting another machine's row for the same third-party UID.
    • Desktop-side patch/delete and async health sink updates now also require matching client_id.
    • Added migration 20260506120000_scope_platform_accounts_to_desktop_client to update the active uniqueness index.
    • Added MAC-address fingerprint fallback for desktop client ID generation while keeping OS machine ID as the preferred stable source.
  • Verification passed:
    • cd server && make sqlc-generate
    • cd server && go test ./internal/tenant/app ./internal/tenant/repository ./internal/tenant/transport -count=1
    • pnpm --filter @geo/desktop-client typecheck
    • pnpm --filter @geo/desktop-client exec vitest run src/main/device-id.test.ts
    • git diff --check
  • Caveat:
    • pnpm --filter @geo/desktop-client test -- src/main/device-id.test.ts ran unrelated existing tests and failed in deepseek.test.ts due to a pre-existing missing electron/main mock; the new device-id test passes when run directly with vitest.

2026-05-07T11:26:26+08:00 - Admin-web publish management start

  • Started Phase 48/49 for the user's content-management "发文管理" request.
  • Confirmed desktop-client already has a publish management table with the requested columns/actions, but admin-web needs tenant HTTP APIs because Electron IPC is unavailable in the browser.

2026-05-09T17:40:00+08:00 - Desktop publisher page.evaluate packaging fix

  • Investigated page.evaluate: ReferenceError: wO is not defined reported for desktop-client Bilibili publishing and then confirmed dongchedi has the same risk pattern.
  • Root cause: packaged desktop builds run build:obf; scripts/obfuscate.cjs obfuscated out/main/*.cjs, and Playwright page.evaluate callbacks then carried obfuscator helper names into the browser page context where those helpers do not exist.
  • Updated apps/desktop-client/scripts/obfuscate.cjs so the Electron main bundle is left unobfuscated and only renderer assets are obfuscated. This aligns the post-build obfuscation step with the existing Vite main-process minify: false guard.
  • Verification passed:
    • pnpm --filter @geo/desktop-client typecheck
    • pnpm --filter @geo/desktop-client build:obf
    • pnpm --filter @geo/desktop-client exec vitest run src/main/adapters/dongchedi.test.ts
    • pnpm --filter @geo/desktop-client exec vitest run src/main/adapters/baijiahao.test.ts
    • Searched apps/desktop-client/out/main/bootstrap.cjs for wO, _0x..., stringArray, javascript-obfuscator, and ['evaluate']; no obfuscator helper traces were present after build:obf.
  • Scoped implementation to tenant publish task list/retry endpoints plus a new admin-web /publish-management page under 内容管理.
  • Noted existing dirty files in i18n/Knowledge/TemplateWizard/config/swagger/knowledge service and will avoid reverting or overwriting unrelated changes.

2026-06-23T23:54:39+08:00 - Doubao thinking-reference fallback

  • Investigated the Doubao monitor failure where the answer page showed "已完成思考 / 搜索 N 个关键词,参考 N 篇资料" links but the adapter returned unknown.
  • Current Doubao behavior observed from user screenshots:
    • citation/source links now live inside the thinking block rather than next to the final answer;
    • one answer can contain multiple "搜索 X 个关键词,参考 Y 篇资料" groups;
    • "已完成思考,参考 N 篇资料" is a broad thinking summary control, while the search groups hold the concrete blue source links;
    • already-expanded groups may still show a chevron, so clicking blindly can collapse useful references.
  • Updated apps/desktop-client/src/main/adapters/doubao.ts so /chat/completion and text/event-stream responses remain the preferred SSE source, while a high-quality page answer can be used when SSE has not finished.
  • Strengthened page fallback to expand/reference-scan Doubao's thinking search groups, skip already-expanded nearby-link groups, merge multiple thinking reference groups, and filter favicon/asset CDN links.
  • Added helper coverage in doubao.test.ts for unfinished SSE + usable page answer, finished SSE precedence, recoverable timeout with anchored page answer, and multi-group thinking references.
  • Follow-up fix: the unknown message "未从正文或思维链兜底抓到可提交答案,仅抓到参考资料" still happened because the implementation only used domAnswer as the fallback answer and did not submit domReasoning. The adapter now allows a high-quality thinking-chain text fallback when SSE/page answer text is missing but references and anchored page context were captured.
  • Local anonymous Playwright could open Doubao only to the logged-out/home page, so live account-specific MCP/page content was not directly readable in this session.
  • Verification passed:
    • pnpm --filter @geo/desktop-client exec vitest run src/main/adapters/doubao.test.ts
    • pnpm --filter @geo/desktop-client typecheck
    • pnpm --filter @geo/desktop-client test
    • git diff --check

2026-06-24T01:20:00+08:00 - Doubao SSE capture and challenge handling

  • Confirmed from the latest user screenshot that Doubao can trigger an official-site image captcha after a prompt is submitted. This is an account/platform challenge, not an answer parsing failure.
  • Hardened apps/desktop-client/src/main/adapters/doubao.ts:
    • capture Doubao /chat/completion SSE in-page through fetch/XHR stream cloning and keep SSE as the primary answer/source path;
    • keep DOM/page extraction as fallback, including thinking/reference panels for search_results without promoting reference lists to answer;
    • verify composer write/readback before sending, avoid selecting voice/microphone controls as send buttons, and add a Playwright keyboard/mouse submit fallback when the page does not observe the user question;
    • detect Doubao captcha/risk-control surfaces such as image selection, drag-to-area, human verification, security verification, and frequent-access prompts;
    • return doubao_challenge_required as failed/non-retryable AI platform authorization when a captcha is visible, so the desktop UI alerts the user instead of writing unknown.
  • Updated apps/desktop-client/src/main/platform-auth-adapters.ts so doubao_challenge_required is classified as a challenge.
  • Updated apps/desktop-client/src/main/runtime-controller.ts so Doubao challenge states show a specific user-facing activity: complete Doubao human verification in the desktop client before continuing collection.
  • Verification passed:
    • pnpm --filter @geo/desktop-client exec vitest run src/main/adapters/doubao.test.ts src/main/platform-auth-adapters.test.ts
    • pnpm --filter @geo/desktop-client typecheck
    • git diff --check
    • pnpm --filter @geo/desktop-client build
  • Remote SSH to 39.105.229.239 with the provided credentials failed with authentication failure, so remote logs/deployment could not be inspected in this pass.

2026-06-24T02:22:00+08:00 - Doubao captcha display, admission, and default mode

  • Updated desktop HomeView issue aggregation so Doubao human-verification/account challenge failures collapse into one actionable item: "豆包需要人工验证"; the summary count now follows displayed issue rows instead of raw failed task rows.
  • Added monitor admission filtering for blocked AI platforms:
    • desktop runtime computes an eligible monitor platform whitelist from current local account health;
    • Doubao challenge_required/expired/revoked states are excluded from local monitor selection, desktop-task monitor lease fallback, legacy monitoring lease fallback, and resume calls;
    • other AI platforms remain eligible, so a Doubao captcha no longer blocks Qwen/Kimi/DeepSeek/etc. work.
  • Extended /api/desktop/tasks/lease with optional platform_ids for monitor leases and filtered the server-side monitor candidate SQL by that whitelist.
  • Changed Doubao execution to use the default visible mode directly:
    • removed active in-page switching to "专家"/"思考" and removed deep-think selection parameters from Doubao query execution;
    • provider_model now reports doubao-web-fast for default/fast and for any observed expert label, with no doubao-web-expert remaining.
  • Verification passed:
    • pnpm --filter @geo/desktop-client exec vitest run src/main/adapters/doubao.test.ts src/main/platform-auth-adapters.test.ts
    • pnpm --filter @geo/desktop-client typecheck
    • pnpm --filter @geo/desktop-client build
    • cd server && go test ./internal/tenant/app ./internal/tenant/transport
    • git diff --check

2026-06-24T02:29:25+08:00 - Doubao DOM source strictness

  • Refined Doubao citation extraction after the user clarified that captcha/risk-control pages may legitimately have no reference sources.
  • Kept SSE as the preferred and broad source path, including structured references that embed Doubao redirect URLs in source payloads.
  • Tightened DOM/page fallback so search_results only comes from real link/card URLs and redirect URL attributes; gray search-query/explanatory text such as "search N keywords, reference N sources" is no longer converted into citation sources.
  • Removed the page-side fallback that scraped arbitrary http URLs from element text, so expanded blue reference links are still captured while gray search keyword text is ignored.
  • Verification passed:
    • pnpm --filter @geo/desktop-client exec vitest run src/main/adapters/doubao.test.ts src/main/platform-auth-adapters.test.ts
    • pnpm --filter @geo/desktop-client typecheck
    • pnpm --filter @geo/desktop-client build
    • git diff --check

2026-07-12T12:15:00+08:00 - Hunyuan SSE/search and Markdown diagnosis

  • Started Phase 50 for the requested Hunyuan collection repair.
  • Locked the desired execution order to official API SSE first, page RPA only when SSE is missing or unusable.
  • Included explicit web-search enablement and end-to-end Markdown display repair in scope.
  • Added an API-priority regression test at the final Yuanbao answer-selection boundary.
  • Confirmed the test fails because the current quality score selects a longer DOM/tooling string over valid SSE Markdown.

2026-07-12T00:00:00+08:00 - Desktop Hunyuan/Yuanbao CDP diagnosis resumed

  • Confirmed npx and playwright-core are available and connected Playwright to the running Electron CDP endpoint on port 9339.
  • Located two authenticated Yuanbao chat pages and inspected the live composer/tool controls.
  • Reproduced a web-search verification defect: the page had only 深度思考 active while 联网搜索 remained an unselected tools-menu item, but the adapter can optimistically return enabled when the menu item exposes no ARIA state.
  • Captured apps/desktop-client/output/playwright/yuanbao-tools.png as local visual evidence.
  • Existing API-vs-DOM priority regression test remains red pending the implementation fix.
  • Submitted a short search-enabled query through the real desktop Yuanbao page over CDP and captured the safe request/response shape.
  • Verified the official chat SSE endpoint, real search request flags, final meta.endConv, and the large ratio of deepSearch process frames to answer text frames.
  • Added regression coverage for strict API priority, page fallback, current-chat SSE filtering, Markdown blank-line preservation, and exclusion of nested deep-search text from the final answer.
  • Initial live-test attempt selected a stale hard-coded conversation ID and failed before adapter execution. The selector was corrected to any current Yuanbao chat page.
  • One CDP probe called browser.close() and stopped the attached Electron Chromium. The existing electron-vite development process restarted Electron automatically; subsequent probes exit without sending a remote close command.
  • Opened the bound Yuanbao account again through the desktop AI-platform card and ran the actual updated adapter through CDP.
  • Live adapter verification passed: API SSE source selected, web search verified active, exactly one current chat SSE captured, and answer non-empty.
  • Final verification passed:
    • pnpm --filter @geo/desktop-client exec vitest run src/main/adapters/yuanbao.test.ts (16/16)
    • pnpm --filter @geo/desktop-client typecheck
    • pnpm --filter admin-web typecheck
    • pnpm --filter @geo/desktop-client build
    • pnpm --filter admin-web build (existing large-chunk warning only)
    • targeted Prettier formatting and git diff --check

2026-07-12T13:30:00+08:00 - AI platform multi-account support started

  • Added Phases 53-56 for storage/runtime audit, binding/UI changes, same-platform monitor failover, and verification.
  • Initial hypothesis: server uniqueness already permits multiple platform UIDs per desktop client, while desktop projection/binding and monitor task targeting still enforce effective single-account behavior.
  • Phase 53 audit completed. No server migration is needed; implementation is scoped to desktop dedupe/binding, platform-blocking, local account-pool failover, diagnostics, and the AI account-management UI.
  • Added a tested monitor-account pool with preferred-first/LRU ordering, blocked/cooldown filtering, failover classification, and reason-specific cooldowns.
  • Removed desktop AI platform-only deduplication and same-platform replacement cleanup; distinct platform UIDs now retain independent server rows and local session partitions.
  • Added same-platform monitor attempts inside one leased task, final-result-only submission diagnostics, account health reporting, runtime switch activities, and per-account risk-control alerts.
  • Rebuilt AI Platform Management as a media-account-style multi-account page and verified its desktop layout over Electron CDP.

2026-07-12T14:20:00+08:00 - AI platform multi-account server invariant

  • Confirmed server monitor targeting remains one target per platform even when several same-platform accounts are bound.
  • uniqueMonitorDesktopTaskPlatformIDs() de-duplicates requested platforms before account lookup, and selectMonitorDesktopTaskTargetsWithOptions() selects a single best target from each platform's account candidates.
  • Added focused assertions covering duplicate platform specs and the one-target result for multiple same-platform accounts.

2026-07-12T14:35:00+08:00 - AI platform multi-account verification complete

  • Focused server targeting tests passed, including duplicate platform de-duplication and one-target selection for multiple same-platform accounts.
  • Focused desktop account-pool, account-health, scheduler, and Doubao tests passed: 3 files / 38 tests.
  • Full desktop verification passed: 30 files / 206 tests, Vue typecheck, production Electron build, targeted Prettier check, and git diff --check.
  • Electron CDP UI verification remains valid at 1420x960: six platform selectors, three separately managed AI accounts, add-account choices for all six platforms, working Doubao filtering, and no overflow or overlap.
  • Residual live-test limitation: the UI was exercised with real bound accounts, but automatic failover could not be triggered against two simultaneously usable Doubao accounts in this environment. The selection, cooldown, error classification, and final-result-only behavior are covered by deterministic tests.

2026-07-12T14:50:00+08:00 - AI/media account UI alignment started

  • Added Phases 57-58 after the user clarified that AI Platform Management must visually match Media Account Management, not only expose equivalent account-management functions.
  • Audited both live Vue views and identified the mismatch: AI used compact selector cards plus a custom account table, while media uses larger authorization cards and a shared hero-card table shell.
  • Locked the change to renderer composition and styles; multi-account runtime behavior and account actions remain unchanged.
  • First Electron CDP comparison command failed because playwright-core is workspace-local and cannot be resolved from the repository root. The retry runs from apps/desktop-client, where the dependency is installed.

2026-07-12T15:15:00+08:00 - AI/media account UI alignment implemented

  • Removed the global “添加 AI 账号” dropdown; platform cards are now the only binding entry, matching the media account workflow.
  • Replaced compact AI selectors with the media page's large card geometry, status dot, bound/unbound treatment, and “新增账号 / 绑定账号” actions.
  • Rebuilt the AI authorized-account list with the same hero-card shell, toolbar, filters, transparent table header, row spacing, avatar/platform cells, status tags, and three-action layout used by Media Account Management.
  • Retained AI-specific multi-account counts, account health/session data, reauthorization/probe behavior, and row-level risk-control warnings.
  • Electron CDP desktop and narrow-width comparisons passed with matching geometry and no page overflow.

2026-07-12T15:25:00+08:00 - Missing-citation failover diagnosis

  • Confirmed the existing account-pool classifier returned success as soon as any non-empty answer existed, regardless of empty citations and search_results arrays.
  • Added a red regression test proving a source-less successful answer previously returned no failover reason.
  • Added missing_citations as a current-task failover reason. It has zero account cooldown because missing sources can be question-specific and should not mark the account unhealthy for later tasks.

2026-07-12T15:35:00+08:00 - AI account UI and missing-citation failover verified

  • Full desktop tests passed: 30 files / 206 tests.
  • Desktop Vue typecheck and production Electron build passed.
  • Targeted Prettier checks and git diff --check passed.
  • CDP confirmed AI and media platform cards have matching geometry at 1420x960; the AI page also passed 1024x800 responsive inspection with no page overflow.
  • The live DOM contains only the hero actions “一键检测账号” and “刷新状态”; the removed global “添加 AI 账号” button is absent.
  • Successful answers now require at least one item in citations or search_results to stop same-platform account failover. When every account lacks sources, the existing loop preserves and submits the final non-empty answer with attempt diagnostics.

2026-07-12T15:50:00+08:00 - Admin AI account management extraction started

  • Added Phases 60-62 for a new tracking-side “AI 账号管理” menu after “外部文章标记”.
  • Confirmed the existing tracking status panel collapses multiple accounts to the first account per platform even though the API returns every account.
  • Confirmed account removal and delete-request APIs can be reused from Media Account Management; implementation needs no server migration.
  • Browser verification fallback note: the first isolated Playwright mock command failed because run-code expects one callback expression rather than consecutive top-level statements. No page state or backend data was changed; the retry wraps the setup in one async callback.
  • The callback retry reached API interception but the CLI sandbox did not expose the global URL constructor. Replaced URL parsing with deterministic request-string normalization before rerunning the isolated fixture.
  • The first successful fixture execution remained on login because the SPA auth store had initialized before test local storage was written. Moved the isolated session seed to page.addInitScript so it runs before the next document and Pinia bootstrap.

2026-07-12T16:40:00+08:00 - Admin AI account management extraction complete

  • Added “AI 账号管理” immediately after “外部文章标记” and moved account status out of Data Details.
  • The new admin page renders one card per AI account, preserves same-platform accounts independently, and reuses the tenant account health, node, delete-request, and unbind lifecycle.
  • Matched /media live geometry: toolbar 74.78px and standard account card 270.09px at desktop width; mobile verification showed no horizontal overflow.
  • Browser network interception added a second Qwen account temporarily; both Qwen cards rendered independently and total card count increased from three to four.
  • Confirmed all three live AI account avatars load as real remote images in the admin browser.
  • Fixed the desktop AI authorized-account table to render record.avatarUrl; initials now appear only when an account has no avatar.
  • Verification passed: admin production build, desktop-client Vue typecheck, targeted Prettier formatting, browser desktop/mobile inspection, and git diff --check.