Files
agent-desk-ng/AGENTS.md
mono 63e0457886 fix: harden identity, live access, drafts and setup (review only)
Source-reviewed hardening changes and regression tests. UNBUILT / UNTESTED: not a deployable release. Go formatting, compilation, automated regression tests, full frontend validation and staging acceptance remain required. See docs/hardening/VALIDATION.md. Main remains the original clean snapshot; no upstream Git history imported.
2026-10-16 12:15:20 +00:00

25 KiB

AGENTS.md

This file defines the mandatory working agreement for AI agents in this repository. It is intentionally based on the current codebase rather than historical conventions.

1. Scope and Priorities

  • These rules apply to the repository root and every subdirectory.
  • Explicit user instructions take precedence over this file. Mention any deliberate deviation in the final summary.
  • Inspect the relevant implementation before editing. Reuse current helpers, component APIs, generated-code workflows, and neighboring patterns instead of relying on memory.
  • Keep changes narrowly scoped. Preserve unrelated and user-owned worktree changes, including staged changes.
  • A review, investigation, or diagnosis request is read-only unless the user also asks for implementation.

2. Current Architecture

The repository contains three application areas:

  • Go server: cmd/server and internal/*
  • Next.js application: web/*
  • Embedded workflow editor: flowgram-editor/*, built into web/public/flowgram-editor

The main stack is:

  • Go 1.26, Gin, GORM, github.com/mlogclub/simple
  • SQLite and MySQL
  • Next.js 16 App Router, React 19, TypeScript, Tailwind CSS, shadcn/Base UI
  • pnpm for both frontend projects
  • Optional LanceDB builds through CGO; Qdrant is also supported by the application

Important entry points:

  • Server assembly and middleware: internal/bootstrap/server.go
  • Explicit API routes: internal/bootstrap/routes.go
  • Model registration: internal/models/models.go
  • Schema/data migration startup: internal/bootstrap/migration.go
  • CRUD generator: cmd/generator/generator.go
  • Frontend enum generator: cmd/enums/generator.go
  • Frontend API client: web/lib/api/client.ts
  • Frontend i18n: web/i18n/* and web/messages/*
  • Dashboard shared components: web/components/dashboard/*
  • Project commands: Taskfile.yml

3. General Change Rules

  • Read the actual type, function, or component signature before using it.
  • Prefer the highest-level existing abstraction that fits the requirement. Do not duplicate query state, pagination, auth refresh, localization, or dashboard CRUD behavior.
  • Do not edit generated artifacts by hand. Change their source and run the corresponding generator/build command.
  • Do not add a second implementation style when the repository already has a shared path for the same concern.
  • Use log/slog for new Go logging and structured key-value fields for relevant context.
  • New Go code uses any, not interface{}. Existing generated or legacy code does not need unrelated cleanup.
  • Secrets, tokens, credentials, and private customer data must never be committed or printed in logs/tests.

4. Go Backend

4.1 Layer Ownership

The normal dependency and data flow is:

models -> repositories -> services -> handlers -> builders/response DTOs

  • internal/models: entity fields, GORM mappings, associations, and schema metadata only.
  • internal/repositories: GORM/SQL access, conditions, ordering, pagination, locks, and persistence details.
  • internal/services: business validation, state changes, authorization-independent domain rules, aggregation, transactions, and event orchestration.
  • internal/handlers/{api,dashboard,third}: HTTP parameter parsing, authentication/permission checks, service calls, and response writing.
  • internal/builders: pure model/aggregate-to-response mapping. Builders must not query the database.
  • internal/pkg/dto/request and internal/pkg/dto/response: external request/response contracts.

Mandatory boundaries:

  • Handlers must not call repositories or issue GORM queries directly.
  • Models and repositories must not contain HTTP, permission, or cross-resource workflow logic.
  • GORM models must not be returned directly from an API. Map them to response DTOs/builders.
  • A service may return models internally, but public response shape remains owned by builders/DTOs.
  • Put reusable SQL in repositories. A genuinely one-off aggregate query may stay near its domain service only when extraction would make ownership less clear.

4.2 Database and Transactions

  • Repository methods that participate in transactions accept db *gorm.DB; call them with sqls.DB() outside a transaction and ctx.Tx inside one.
  • Services own transaction boundaries through sqls.WithTransaction.
  • Use a transaction for atomic multi-write workflows and consistency-sensitive read-modify-write operations.
  • Do not add a transaction around a single independent SQL write.
  • Every database operation inside a transaction must use the same ctx.Tx; never escape to sqls.DB() mid-transaction.
  • Use sqls.Cnd/sqls.NewCnd() and repository methods for ordinary filtering and pagination.
  • Preserve SQLite and MySQL compatibility. Avoid dialect-specific SQL unless both dialects are explicitly implemented and tested.
  • Use portable column types and int64 primary/foreign identifiers. Keep time handling compatible with MySQL parseTime=True.

4.3 Models, Generation, and Migrations

  • Register persistent models in internal/models/models.go so startup AutoMigrate(models.Models...) includes them.
  • New tables, columns, indexes, and compatible constraints are normally applied by GORM AutoMigrate in internal/bootstrap/migration.go.
  • Use internal/migration/* only for versioned, idempotent data migration/backfill/repair work. Its version must increase monotonically.
  • When a model uses the standard generated repository/service surface, register it in cmd/generator/generator.go and run task generator.
  • Treat generator output as mechanical infrastructure. Put business-specific methods in handwritten files and do not manually patch generated CRUD output.
  • Backend/frontend shared enums are defined in internal/pkg/enums, annotated using the existing enum pattern, and generated with task enums into web/lib/generated/enums.ts.
  • Never create a handwritten frontend duplicate of a generated backend enum.

4.4 HTTP APIs

  • All routes are explicit in internal/bootstrap/routes.go; handler names do not create endpoints.
  • Public/product APIs live under /api/*, authenticated management APIs under /api/dashboard/*, callbacks under /api/third/*, and WebSockets under /api/ws/*.
  • Add a resource-specific register...Routes function or extend the existing one, then mount it from addRouter under the correct group.
  • Follow the existing resource contract: detail commonly uses GET /:id, list uses /list, writes use explicit POST actions such as /create, /update, /delete, and domain actions retain their established snake_case path names.
  • Do not introduce /api/v1, automatic routing assumptions, or unnecessary deeply nested resource paths.
  • Handler names mirror the registered method and path, for example XxxGetBy, XxxAnyList, and XxxPostCreate.
  • Parse JSON/form/query/path values with internal/pkg/httpx/params and internal/pkg/httpx helpers.
  • Dashboard permission checks use services.AuthService.RequirePermission or the established permission helper before domain work.
  • Write responses through httpx.WriteJSON; preserve the shared JsonResult contract.
  • Paginated responses use web.PageResult with data.results and data.page.
  • Convert not-found, validation, permission, and persistence failures into stable application errors. Never expose raw SQL errors to clients.

4.5 Backend Internationalization

  • Any new or changed user-visible backend error must support every backend locale, currently zh-CN and en-US.
  • Add matching keys to both internal/pkg/i18nx/locales/zh-CN.yml and internal/pkg/i18nx/locales/en-US.yml in the same change.
  • Services should return localized application errors through the errorsx.*I18n helpers.
  • Handlers that need an immediate localized response use httpx.JsonErrorMsg(ctx, key, args...); other request-context translation uses i18nx.T.
  • Do not hard-code Chinese or English error sentences in handlers/services when the text can reach a user.
  • Keep format arguments equivalent across locales and cover new reusable/error-format behavior with focused tests.

5. Frontend

5.1 Component and Data Boundaries

  • Application routes live under web/app; reusable business components live under web/components; route-private components live in the route's _components directory.
  • Reuse web/components/ui/* primitives. Do not edit these shadcn base components for a feature-specific requirement.
  • Use the current Base UI/shadcn component API as implemented in the repository; do not assume APIs such as Radix asChild exist.
  • Use @/* imports for code within web.
  • Client components must declare "use client" when they use state, effects, browser APIs, or client-only hooks.
  • Keep resource APIs in web/lib/api/* and route all normal requests through web/lib/api/client.ts.
  • Pages, business components, and stores must not implement their own JsonResult parsing, auth header handling, token refresh, or login-expiry cleanup.
  • Direct fetch is reserved for unsupported transports such as third-party calls, binary transfers, SSE, and WebSocket handshakes; explain the exception in code.
  • Prefer OptionCombobox for standard dropdowns rather than adding shadcn Select-based business controls.
  • Format displayed timestamps with formatDateTime from web/lib/utils.ts unless the product explicitly requires a different representation.

5.2 Dashboard Pages

Before building a dashboard page, inspect web/components/dashboard/crud, web/components/dashboard/list, and web/components/dashboard-page.tsx.

Use this order of preference:

  1. DashboardCrudPage for standard create/read/update/delete resources.
  2. DashboardListPage for read-only or custom-content paginated resources.
  3. useDashboardPagedList when layout is bespoke but list query/filter/pagination lifecycle is standard.
  4. DashboardPage, DashboardToolbar, DashboardTableShell, and related low-level primitives only for interactions that cannot fit the higher-level components.

Rules:

  • Do not copy a resource page to recreate standard filters, query/reset/refresh actions, pagination, loading/empty states, confirmations, row actions, or dialogs.
  • Configure DashboardCrudPage through its filters, columns, labels, service callbacks, row actions, sorting, and form/dialog extension points before adding page-local infrastructure.
  • Use its schema-driven DashboardCrudFormDialog when supported. A genuinely custom form may live in _components and should normally use react-hook-form, zod, and Field.
  • Configure DashboardListPage with columns or renderContent, and use renderToolbarActions for resource-specific actions.
  • Business API knowledge stays in the page/service module; generic dashboard components must not import a resource-specific API.
  • A change to web/components/dashboard/* must be generic, backward-compatible, and useful beyond one page. Otherwise keep it local to the feature.

5.3 Frontend Internationalization

  • Every frontend feature and modification must work in all SUPPORTED_LOCALES, currently zh-CN and en-US.
  • Add every new key to both web/messages/zh-CN.json and web/messages/en-US.json in the same change, preserving matching structure.
  • React pages/components use useI18n(); locale-aware formatting/mapping may also use useAppLocale().
  • Non-React code uses translateCurrentMessage() or translateMessage() from web/i18n/messages.ts.
  • Do not hard-code user-visible copy in JSX/TSX, toast messages, dialogs, confirmations, placeholders, validation, empty/loading/error states, tooltips, accessibility labels, or client-side fallback errors.
  • Product names, protocol literals, user content, and raw business data do not need translation unless the UI already provides a display-name mapping.
  • Dashboard labels supplied to shared CRUD/list components must come from translation keys.
  • Centralize localized display names for backend identifiers/enums in a reusable web/lib/*-i18n.ts helper instead of duplicating locale switches across pages.
  • Preserve the same placeholders in every locale and interpolate with t(key, values); do not assemble sentences by concatenating translated fragments.
  • Locale configuration belongs to AppI18nProvider and web/i18n/config.ts; features must not introduce separate locale detection or state.

5.4 Commercial-Grade UI and Interaction

This is a commercial product, not a prototype or component demo. UI work is complete only when it is visually coherent, interaction-complete, responsive, localized, and credible with real production data.

Visual Quality

  • Follow the existing design language, spacing scale, typography, radius, color tokens, and component variants. A new feature must look native to the product rather than like a pasted template.
  • In support platform UI, if rounded corners are needed, use rounded-md consistently.
  • Support platform components should not add shadow.
  • Establish a clear hierarchy: page title/primary action, filters or context, main content, and secondary information. Do not make every region a card or every action visually prominent.
  • Prefer restrained, purposeful styling. Avoid decorative gradients, oversized hero text, excessive shadows, glass effects, emoji icons, random accent colors, and ornamental copy unless the product context explicitly calls for them.
  • Use Lucide icons consistently. Choose icons by meaning, keep icon size and stroke weight aligned with neighboring controls, and never use icons as decoration without communicative value.
  • Keep spacing and alignment deliberate at every breakpoint. Labels, inputs, table columns, action groups, dialog footers, and empty states must align cleanly without ad hoc offsets.
  • Design for realistic content, not ideal sample text. Verify long names, multiline content, large counts, missing optional fields, and mixed Chinese/English values. Use wrapping, truncation, tooltips, or scroll containers intentionally.
  • Preserve information density appropriate to an operations dashboard. Do not waste large areas on decoration, but do not compress controls until scanning and clicking become difficult.

Interaction Completeness

  • Every asynchronous action must have an immediate and unambiguous state: pending/loading, success, failure, and retry or recovery when appropriate.
  • Prevent duplicate submissions. Disable or lock the initiating control while a mutation is pending and show action-specific progress text or a spinner without causing layout shift.
  • Keep feedback close to the action. Use inline validation for field problems, contextual error states for failed content, and toast notifications for completed background or page-level actions.
  • Never silently discard user input. Warn before closing, navigating away, resetting, or switching context when there are meaningful unsaved changes.
  • Destructive, irreversible, security-sensitive, or broad-impact operations require confirmation that clearly names the object and consequence. Do not use a generic “Are you sure?” message.
  • Confirmation is not a substitute for good defaults: routine reversible actions should remain efficient and should not be interrupted by unnecessary modal prompts.
  • After create/update/delete operations, keep list state coherent: refresh affected data, preserve useful filters/page position when possible, close dialogs only on success, and prevent stale selections or detail panels.
  • Buttons and menu items must use precise verbs describing the result. Avoid vague labels such as “OK”, “Submit”, or “Process” when a specific action name is available.
  • Preserve keyboard behavior and focus flow: Enter submits only where expected, Escape closes dismissible overlays, focus moves into dialogs and returns to the trigger, and destructive actions are not the accidental default.
  • Interactive rows, icons, badges, and text links must look interactive only when they are interactive. Do not rely on hover-only discoverability for essential actions.

Forms and Dialogs

  • Use the smallest suitable interaction container: inline editing for simple local changes, a dialog for focused tasks, and a full page/workbench for complex or multi-step workflows.
  • Dialogs need a clear title, concise context when necessary, stable body layout, and a consistent footer with secondary action before primary action. Long content must scroll inside the dialog without pushing actions off-screen.
  • Forms must have visible labels, appropriate controls, useful defaults, required/optional semantics, and examples or help text only where they reduce ambiguity.
  • Validate at the right time: do not show errors before the user has interacted, clear stale errors after correction, and map backend validation failures to the relevant field when possible.
  • Preserve entered values after failed submission. Do not reset or close a form until the server confirms success.
  • Dependent fields must clearly reflect disabled/loading/empty states. When one field invalidates another, update it predictably and explain the dependency when it is not obvious.
  • Configuration and high-impact forms should not expose a permanently editable surface when a deliberate edit mode improves safety. Saving sensitive configuration requires explicit user intent and appropriate confirmation.

Lists, Tables, and Operational Screens

  • Use the shared Dashboard CRUD/list system and maintain consistent toolbar, filter, pagination, loading, empty, and action placement across resources.
  • Filters must distinguish draft values from applied query state. Query, reset, refresh, pagination, and URL/state behavior should be predictable and must not unexpectedly erase one another.
  • Tables must remain scannable: align comparable values, keep action columns stable, use badges sparingly for status, avoid dense multiline cells when a detail view is more appropriate, and provide horizontal overflow on narrow screens.
  • Empty states must distinguish “no data exists” from “no results match the current filters” and offer the most relevant next action when the user can resolve the state.
  • Loading states should preserve layout. Prefer skeletons for content whose shape is known and compact spinners for localized actions; avoid replacing an entire stable page with a centered spinner.
  • Error states must explain what failed in user terms and provide retry when retry is meaningful. Never leave a blank table or empty panel after a request failure.
  • Bulk actions must show selection count, affected scope, eligibility, and partial-failure results. Clear selection when it is no longer valid.

Responsive and Accessibility Requirements

  • Every changed screen must work at desktop and narrow/mobile widths. Do not treat horizontal clipping, overlapping controls, wrapped action chaos, or off-screen dialog buttons as acceptable.
  • Responsive behavior must preserve task priority: primary actions remain reachable, secondary actions may move into menus, filters may stack or collapse, and tables may scroll without hiding row identity/actions.
  • Use semantic controls and accessible names. Icon-only buttons require localized accessible labels and tooltips where the meaning is not universally obvious.
  • Maintain visible focus states, logical tab order, sufficient target sizes, and adequate text/background contrast. Do not encode status or errors using color alone.
  • Respect reduced-motion preferences. Animations should explain state or continuity, remain subtle, and never delay work.

Product Copy and Data Credibility

  • Copy must sound like a finished product: concise, specific, consistent, and action-oriented. Do not expose implementation jargon, placeholder prose, “TODO”, mock labels, debug wording, or developer instructions to users.
  • Do not ship fake statistics, sample records, disabled-looking placeholder buttons, decorative charts without meaning, or interactions that only log to the console.
  • Distinguish unavailable features from empty data. If a capability is not implemented, do not render a control that pretends it is functional.
  • User-visible names, statuses, permissions, dates, and errors must use the established formatting and i18n mappings rather than raw backend identifiers.

UI Verification

  • For meaningful UI changes, inspect the finished screen in a real browser at representative desktop and narrow widths. Source review and typecheck alone are not sufficient visual validation.
  • Exercise the complete interaction, including initial load, populated state, empty/filtered state, validation failure, server failure when practical, pending/disabled behavior, success, cancel/close, and refresh.
  • Check both zh-CN and en-US; verify that translated copy does not overflow, truncate critical meaning, or break control alignment.
  • Before handoff, remove temporary data, debug UI, console output, test-only shortcuts, and visual artifacts introduced during verification.
  • When browser verification cannot be performed, state that limitation explicitly; do not describe the UI as visually verified.

5.5 Generated and Embedded Frontend Assets

  • web/lib/generated/enums.ts is generated by task enums; do not edit it manually.
  • web/public/sdk/agent-desk-sdk.min.js is generated from the SDK source. When SDK source changes, run cd web && pnpm build:sdk and its focused SDK tests.
  • web/public/flowgram-editor is produced from flowgram-editor; edit the source project, not the generated public output.
  • When changing flowgram-editor, use its own pnpm scripts and ensure the embedding build still succeeds.
  • The Next.js application is statically exported/embedded into the Go binary. Avoid runtime-only Next.js features that conflict with the current export and embedding model.

6. Testing and Validation

Validation must match the changed surface. Do not claim checks that were not run.

  • Go formatting: run gofmt on every changed .go file.
  • Go behavior: add focused tests for changed business rules, transactions, security boundaries, parsing, and reusable helpers; run the narrow package tests first, then go test ./... when practical.
  • Frontend TypeScript: run cd web && pnpm typecheck for frontend changes.
  • Frontend lint: run cd web && pnpm lint for broader component/page changes or when lint-sensitive code changed.
  • Frontend logic: run relevant node --test ... files when changing utilities, i18n mappings, generated SDK behavior, or other modules with focused tests.
  • Workflow editor: run the relevant flowgram-editor lint/build commands for changes in that project.
  • Generation: after model CRUD or shared enum changes, run the corresponding task generator or task enums and review generated diffs.
  • Build: use task build when changes affect frontend embedding, build configuration, generated public assets, or release assembly.
  • Browser verification is expected for meaningful visual or interaction changes when a runnable environment is available; state explicitly when it was not performed.
  • Documentation-only changes require at least git diff --check and verification that every referenced path/command exists.

7. Completion Checklist

Before handing off a change, confirm the applicable items:

  • The implementation follows current layer/component ownership and does not create reverse dependencies.
  • Transactions cover exactly the operations that must be atomic, and all transactional DB calls use the same ctx.Tx.
  • API routes are explicitly registered and responses preserve JsonResult/PageResult contracts.
  • Models, migrations, queries, and tests remain compatible with SQLite and MySQL.
  • Backend and frontend user-visible text is complete in both Chinese and English.
  • Dashboard pages reuse the highest-level suitable component under web/components/dashboard/*.
  • UI changes meet the commercial-grade standard: complete states, precise feedback, safe mutations, responsive layout, accessible controls, credible copy/data, and no demo-only behavior.
  • Generated files were regenerated from their source and were not manually edited.
  • Relevant tests/typechecks/lint/build/browser checks were run, and any validation limitation is reported.
  • git diff --check passes and unrelated worktree changes remain untouched.

Independent hardening fork boundaries

Modified in this independent fork. Preserve the upstream license and existing notices. Public history begins from a source snapshot, not the upstream Git history. See docs/hardening/README.md for the source revision and release gate.

  • Treat canonical customer/channel identity, scoped issuer proof, live-session revalidation and permission-filtered WebSocket delivery as security invariants. Never restore raw external-ID topic routing or unscoped public identity lookup.
  • Old customer tokens and legacy identity reconciliation have explicit upgrade implications. Do not auto-merge legacy customers or delete history to make a migration pass; follow docs/hardening/CUSTOMER_IDENTITY_UPGRADE.md.
  • Setup repair previews must bind exact named changes to the caller and current state. Apply requires explicit matching approval and transactional writes. Never resurrect deleted configuration or silently create working schedules.
  • Keep editor drafts, sends and delayed async responses bound to their captured identity and conversation. Clear only the sent draft revision after success.
  • Maintain Russian alongside English and Chinese for every changed UI/API string.
  • Keep docs/hardening/VALIDATION.md accurate. Tests authored but not executed are not passing tests; source parsing is not a build or browser acceptance check.