Skip to content

Structure

npond edited this page Sep 2, 2026 · 1 revision

AutoNate Structure — where do I put this?

Provenance. Generated by /n8-map from the commit named below, and not regenerated since. Individual statements may have been overtaken by later work — most of the issues this map cites have since been fixed and closed. Read it as a snapshot of the codebase's shape, not as a current defect list; the GitHub issue register is authoritative for what is actually open. (The generating commit hash predates a history rewrite and no longer resolves.)

One-line summary: directory-by-directory map of the repo with the rule for adding to each location, the DB schema ownership table, and where tests for each area go.

Generated from commit 01f0f174 on 2026-08-31 by /n8-map.

Companion: Architecture (how the pieces work). Paths are repo-relative; line numbers as of the commit above.


1. Repo root

Path What lives there Rule for adding
AutoNate.sln AutoNate.Web, AutoNate.Plugin.Abstractions, AutoNate.Web.Tests, AutoNate.E2E.Tests, AutoNate.Web.Tests.SamplePlugin, HelloPlugin, Auditor New .NET project (incl. a plugin) must be added here to build with the solution.
Directory.Build.props AnalysisLevel=latest, AnalysisMode=Recommended, EnforceCodeStyleInBuild, analyzers Microsoft.VisualStudio.Threading.Analyzers, AsyncFixer, SonarAnalyzer.CSharp; TreatWarningsAsErrors deliberately unset; no TFM (each csproj sets net10.0) Per-rule tuning goes in root .editorconfig, not here.
Makefile infra-prepare/ensure/up/up-dashboard/down/reset/logs/ps, app (= app-dapr), rider-sidecar*, e2e-install, e2e New dev workflow = new target here; compose services are addressed via COMPOSE := docker compose -f infra/docker-compose.yml.
package.json, scripts/ Root Node tooling: scripts/vendor-bpmn.mjs (+ bpmn-entry.mjs) bundles bpmn-js into src/AutoNate.Spa/public/vendor/bpmn-js; scripts/regen-fa-icons.mjs regenerates src/AutoNate.Spa/src/lib/fa-icons.json Repo-level build scripts only; SPA app code never goes here.
CLAUDE.md, .claude/skills/ Project instructions + 10 skills (add-audit-event, add-page-context-provider, add-permission-gate, add-projection, add-record-event-type, add-workflow-execution-action, plugin-creator, mantine-*) Invoke the matching skill before touching its subsystem; fix drift you find in the same commit. Known drift: add-permission-gate step 6 references a non-existent usePermissionPrefetch; plugin-creator lists 8 of 13 IPluginContext members; add-page-context-provider claims a pageKey-mismatch 400 that no longer exists.
.n8/ n8SDLC config (config.yml: areas api spa plugins services flowable infra ci docs tests), decisions.md, memory/audit-*.md, hot-paths.md Append decisions; audit checklists are shared across audit runs.
docs/ codebase/ (this map), plans/YYYY-MM-DD-kebab.md (design docs referenced from source comments), projection-framework/ (8 docs), samples/, mantine/llms.txt, playwright-test-plan.md, playwright-test-backlog.md New design doc → docs/plans/<date>-<kebab>.md and reference it from the code it governs.
infra/ docker-compose.yml (postgres, flowable, flowable-dapr, redis, nats, nats-init, dapr-placement, dapr-scheduler, hocuspocus, dapr-dashboard), dapr/components/{pubsub,statestore}.yaml (tracked source of truth), postgres/init/01-create-autonate-db.sql + 02-create-autonate-app-schema.sql, flowable/Dockerfile, scripts/bootstrap-jetstream.sh, ensure-up.sh, *-autonate-web-sidecar.sh, mounts/ (untracked runtime data) New infra service → compose + ensure-up.sh readiness + Makefile. New JetStream subject → also NatsStreamProvisioner.cs (they must match). executor sidecar is not in compose — add it if you need code nodes locally.
src/ AutoNate.Web, AutoNate.Spa, AutoNate.Plugin.Abstractions
plugins/ HelloPlugin/, Auditor/, shared Directory.Build.props/.targets, README.md New plugin → plugins/<Name>/ (§6).
services/ hocuspocus/ (Yjs sync edge, :1234), executor/ (NATS code runner) New sidecar → services/<name>/ with Dockerfile, README.md, package.json; wire into compose.
flowable-extension/ Maven module com.autonate:autonate-flowable-events (Java 21) Java changes only; rebuilt by infra/flowable/Dockerfile.
tests/ AutoNate.Web.Tests, AutoNate.E2E.Tests, AutoNate.Web.Tests.SamplePlugin §8.
temp/ (untracked) Scratch: screenshots, captured logs, Playwright snapshots Anything throwaway goes here, never the repo root.

2. src/AutoNate.Web/ — the host

Path What lives there Rule for adding
Program.cs (1636 lines) + Program.Partial.cs All DI + pipeline + endpoint mapping (top-level statements). Registration blocks: cookie auth :87-137, CSRF threat model :151-194, audit/outbox/Dapr :195-262, DbContext :264-280, IDatabaseInitializer :281-285, authorization options :286, entity types :288-296, selector compilers :299-337, instance authorizers :339-348, IAuthorizer :350, content authz :351-383, stores :386-397, AQL entities :399-441, external connections :450-461, data stores/datasets/transformers/pipelines :463-614, agent skills :626-728, ISkillRegistry :730, page-query bridges :732-771, workflow behaviors :776-783, Yjs :784-796, AllowedHosts guard :798-823, system issues :824-895, hooks :897-906, plugin host :907-921, projections :946-992, cold tier :994-1003; app.Map*Endpoints() :1421-1487 Every new service is registered in the block for its area. Endpoint files are mapped in the :1421-1487 list — add app.Map<Thing>Endpoints(); there (content-hierarchy files are grouped after the :1467 comment).
Endpoints/ (63 *Endpoints.cs + HttpContextActorExtensions.cs, SharedSecretEndpointFilter.cs, YjsInternalSecretEndpointFilter.cs, YjsManagedContentGuard.cs, UserDisplayName.cs) One file per route prefix; DTO records at top, public static class XEndpoints, MapXEndpoints(this IEndpointRouteBuilder app), MapGroup("/api/x").RequireAuthorization(), return app; New endpoint fileEndpoints/<Thing>Endpoints.cs + app.Map<Thing>Endpoints(); in Program.cs:1421-1487. Every route gets one gate marker (RequirePermission/RequireKindPermission/AuthorizedInHandler/OpenToAuthenticated/AllowAnonymous) or AuthorizationGatePresenceTests fails. Mutations chain .DisableAntiforgery(); anonymous mutations must use an antiforgery token or a shared-secret filter. Emit view/mutation audit events via IAuditEventPublisher after success.
Authorization/ EntityKinds.cs, Actions.cs, EntityTypeDefinition.cs, EntityRegistry.cs, SystemRoles.cs, AuthorizationOptions.cs, Edges/ (EdgeKinds, EntityEdgeWriter), EndpointFilters/ (RequirePermission*, RequireKindPermissionFilter, AuthorizationDecisionMetadata), EntityTypes/{CoreEntityTypes,AnalyticsEntityTypes}.cs, Evaluator/ (IAuthorizer, Authorizer, IInstanceAuthorizer, InstanceAuthorizers, FlowableInstanceAuthorizers, AuthCacheBumper), Selectors/ (parser, AST, printer, ISelectorCompiler, registry, per-kind compilers, PathOnlySelectorCompiler, RecordSelectorSqlCompiler, InMemorySelectorEvaluator) New EntityKind → const in EntityKinds.csEntityTypeDefinition in EntityTypes/CoreEntityTypes.cs (append to _all) or AnalyticsEntityTypes.cs → compiler in Selectors/<Kind>SelectorCompiler.cs (or PathOnlySelectorCompiler<T>) registered at Program.cs:299-337IInstanceAuthorizer in Evaluator/InstanceAuthorizers.cs registered at Program.cs:339-348 → bump tests/.../Authorization/EntityRegistryTests.cs:15 count → GrantsHelpModal.tsx text. New Action → const in Actions.cs + append to the kind's actions[].
Persistence/ AutoNateDbContext.cs (+ .DataStores.cs, .ProjectionCaches.cs partials), Scaffolded/ (77 EF entities), DatabaseSchemaInitializer.cs, PrimaryDatabaseInitializer.cs (Order 0), IDatabaseInitializer.cs, PersistenceModelMapper.cs, RecordPersistenceMapper.cs, DbConnectionFailureLoggingInterceptor.cs New tableprivate const string <Feature>SchemaSql in DatabaseSchemaInitializer.cs + ExecuteSqlRawAsync line in EnsureAsync (:3746-3826) after its FK targets + entity class in Scaffolded/ + DbSet on the DbContext partial. Stores do NOT go here (see Services/). Secondary databases implement IDatabaseInitializer with Order 10.
Services/<Area>/ (33 areas) Domain services, stores (EfCore<Thing>Store.cs next to I<Thing>Store.cs), event-type consts (<Area>EventTypes.cs), options classes New storeServices/<Area>/I<Thing>Store.cs + EfCore<Thing>Store.cs (primary-ctor IDbContextFactory<AutoNateDbContext>, AsNoTracking, ToModel()), AddScoped in Program.cs. New topic<Area>EventTypes.cs with TopicRoot/TopicName consts, subject in Services/Nats/NatsStreamProvisioner.cs:53-105, transport + category in Services/Events/EventCatalog.cs.
Services/Agent/ Loop/ (AgentSession, AgentOptions, SystemPromptBuilder, compaction), Skills/ (36 skills + IAgentSkill.cs, SkillRegistry.cs, Internal/ConfirmGate.cs, PluginContributedSkill.cs), Providers/ (IChatProvider, Anthropic/OpenAI, ChatProviderResolver), PageQuery/ (channels + routers — framework, do not touch per page), Conversations/, Catalog/ (agent models), Search/ New agent skillServices/Agent/Skills/<Name>Skill.cs implementing IAgentSkill (globally unique tool names; JSON-schema JsonElement; authorize through IAuthorizer/gated stores; ConfirmGate for mutations) + builder.Services.AddScoped<IAgentSkill, <Name>Skill>(); at Program.cs:626-728 + tests in tests/AutoNate.Web.Tests/<Name>SkillTests.cs.
Services/Events/ IAuditEventPublisher/DaprAuditEventPublisher, AuditEventOutbox.cs, AuditOutboxDispatcher.cs, EventCatalog.cs, ViewEventHelpers.cs, AuditContext.cs New audit event → follow .claude/skills/add-audit-event; assert with factory.RecordedAuditEvents.
Services/Records/ Record/type/edge/comment/history stores, Fields/ (IFieldType, FieldTypeRegistry, built-ins), RecordEventPublisher.cs, RecordSchemaEventTypes.cs, RecordFilterCompiler.cs, RecordTypeShortCodeCache.cs, Rollups/ New field type → Fields/<Name>FieldType.cs + registration. New record event type → .claude/skills/add-record-event-type.
Services/Query/ AQL lexer/parser/AST/validator/executor, Entities/ (IQueryEntity, registry, 11 entities), saved queries, share tokens, suggestions New AQL entityServices/Query/Entities/<Name>QueryEntity.cs + double registration at Program.cs:399-441 (AddScoped<X>() + AddScoped<IQueryEntity>(sp => sp.GetRequiredService<X>())).
Services/Flowable/ IFlowableClient/FlowableClient, Cache/ (4 projections + polling feeds, FlowableReadThrough, retention, ColdTier/) New Flowable read path: extend IFlowableClient and tests/AutoNate.Web.Tests/StubFlowableClient.cs.
Services/Workflow/ EfCoreWorkflowModelStore, WorkflowBpmnXml, IWorkflowSignalRegistry, Behaviors/ (WorkflowBehaviorRegistry, UnlockAccountBehavior, WorkflowBehaviorOptions), WorkflowEventTypes.cs, error/task recorders New built-in behaviorBehaviors/<Name>Behavior.cs : IWorkflowBehavior (idempotent) + AddSingleton<IWorkflowBehavior, X>() at Program.cs:776-783.
Services/Signals/ DaprStreamingSubscriber, WorkflowSignalDispatcher Always-on topic list at DaprStreamingSubscriber.cs:388-408.
Services/BusWatcher/ BusWatcherStreamService, Subscriptions/ (SubscriptionManager, ChannelName, SubscriptionProtocol, Resolvers/, Gates/) New WebSocket channel → Resolvers/<X>ChannelResolver.cs + Gates/<X>ChannelSubscribeGate.cs + DI in ScopedSubscriptionsServiceCollectionExtensions.cs.
Services/Projections/ Framework (IProjection, IChangeFeed, ChangeEvent, ProjectionWorker, registry/health/metrics/options, Stores/, Feeds/, BackfillRunner) New projection → follow .claude/skills/add-projection: projection class in the owning area (e.g. Services/Flowable/Cache/), cache table SQL, AddProjection<> + AddChangeFeed<> at Program.cs:946-992.
Services/SystemIssues/ EfCoreSystemIssueStore, Detectors/ (PeriodicIssueDetector base + 10 detectors with nested *Options), Remediators/ (2), SystemIssueRemediationDispatcher, exception traps, CriticalIssueNotifier New detectorDetectors/<Name>Detector.cs : PeriodicIssueDetector + options AddOptions<>().BindConfiguration() + AddHostedService<>() at Program.cs:846-878. New remediatorRemediators/<Name>Remediator.cs : IIssueRemediator + AddSingleton<IIssueRemediator, X>() at Program.cs:894-895.
Services/Content/ IContentAuthorizer/ContentAuthorizer, ContentKinds, ProjectRole, IContentTreeService, IContentVersionService, Bindings/, attachment/import storage, ContentEventTypes.cs New content kind must be added to ContentKinds, EntityKinds, the content_ancestors maintenance, and the ticket-kind map in Endpoints/YjsEndpoints.cs if collaborative.
Services/Yjs/ YjsServerOptions (YjsServer:*), ticketing New Y-doc prefix → also services/hocuspocus/src/materializers.ts and YjsEndpoints.cs:724-731.
Services/DataStores/, Services/Datasets/, Services/DataConnectors/, Services/Transformers/, Services/Analyzers/, Services/Pipelines/ Data platform (§9 of Architecture) New transformerServices/Transformers/Builtin/<Name>Transformer.cs : ITransformer + AddSingleton<ITransformer, X>() at Program.cs:503-530 + entry in BuiltinSchemas.cs. New dataset parserServices/Datasets/Files/<Kind>FileParser.cs : IDatasetFileParser + registry. New data-store kind → append DataStoreKind value, branch Endpoints/DataStoreEndpoints.cs:77, service under Services/DataStores/<Kind>/, extend IDatasetExecutor routing, DI at Program.cs:470-488. New pipeline node kindPipelineNodeKinds + Pipelines/Execution/<Kind>Runner.cs : INodeRunner.
Services/Menus/, Services/SiteSettings/ EfCoreMenuStore, EfCorePageTemplateStore, PageRegistrySnapshotCache; SiteSettingsRegistry, SiteSettingsStore, SiteAppearanceSnapshotCache New site setting → declare in Services/SiteSettings/SiteSettingsRegistry.cs. New page template row → seed SQL in DatabaseSchemaInitializer.cs (PageTemplatesSeedSql, :1325) + SPA key (§3).
Services/Auth/, Services/Authorization/, Services/Audit/, Services/Notifications/, Services/ExternalConnections/, Services/Forms/, Services/Dashboards/, Services/Notes/, Services/Nats/, Services/Dapr/, Services/SystemHealth/, Services/ApplicationEvents/, Services/Common/ Local users + auth events; role/group/assignment/grant stores + IAM events; IRequestContext; notifications + listener; connection store + secret protector; forms; dashboards; markdown→BlockNote; NatsStreamProvisioner; sidecar probe; health probes; app events; helpers Follow the store pattern; new ISystemHealthProbeServices/SystemHealth/.
Hooks/ HookRegistry<T>, ActionHub, FilterHub, HookRegistrar, ScopedHookRegistrar, HookSubscription New hook point → const in src/AutoNate.Plugin.Abstractions/HookPoints.cs + host call site guarded by HasAction/HasFilter; document payload type in the abstractions.
Plugins/ PluginRuntime, PluginHostedService, PluginAssemblyLoadContext (shared-assembly list), PluginManagementService, PluginUploadValidator, PluginManifest, PluginSchemaProvisioner, PluginMigrationRunner, PluginDataAccess*, Plugin{Menus,Behaviors,Projections,AgentSkills,Connectors,Transformers} + Noop*, PluginScheduledJob*, SafePluginServiceProvider, PluginOptions New plugin extension surface → abstraction interface in AutoNate.Plugin.Abstractions, host impl + Noop* here, wire into PluginContext and the enable/disable sweep in PluginRuntime.cs:214-243.
Models/ Hand-written domain models: Records/, Authorization/, Forms/, Menus/, Notifications/, FlowableModels.cs, WorkflowModel.cs, LocalUser.cs Domain model here; EF entity in Persistence/Scaffolded/; mapper in Persistence/*Mapper.cs.
Configuration/, Storage/ InfrastructureOptions (Flowable, Dapr), NatsOptions, TrustedProxyOptions, DevelopmentAutoLoginOptions; DataOptions (Data:Root, Data:PublicUrlPrefix=/files), IDataPaths/DataPaths New writable runtime folder → property on IDataPaths created in the DataPaths ctor, under /data.
appsettings.json, appsettings.Development.json Dev defaults; production overrides via env vars (README "Deployment configuration") New options class → AddOptions<T>().BindConfiguration(T.SectionName); add dev value here.
wwwroot/ Vite dist/ copy (Release/publish) + drawio/ static assets Never edit by hand; BuildSpa target owns it.
data/ Runtime /data root in dev (datastores, plugins, repositories, tmp, uploads, wwwroot, datastores-writer.secret) Gitignored runtime state.

DB schema ownership (Persistence/DatabaseSchemaInitializer.cs)

Each row is a private const string at the given line, executed in EnsureAsync (:3746-3826) in this order unless noted. "Owns" = tables created there.

Line Section const Owns (tables)
9 WorkflowVersioningSql workflow_model_versions (+ workflow_models columns)
112 WorkflowDefaultVariablesSql column adds
118 RecordsSchemaSql record_types, record_type_fields, record_type_audit_log
171 RecordsDataSchemaSql records, record_field_changes
250 RecordsEdgesSchemaSql record_edge_types, record_edge_type_fields, record_edges
305 RecordsCommentsSchemaSql record_comments, record_comment_revisions
338 RecordWatchesSchemaSql record_watches
354 AuthorizationSchemaSql entity_kinds, entity_edges, roles, role_assignments, auth_cache_version, auth_seed_state, groups, group_members, permission_grants
489 / 574 / 605 / 632 RecordEdgeBackfillSql, RecordEdgeShadowBackfillSql, EntityEdgeHotIndexesSql, RolePermissionsToGrantsSql one-shot backfills (gated by auth_seed_state)
537 SuperAdminBackfillSql conditional on Authorization:AssignSuperAdminToAllExistingUsers
663 / 698 PageTemplatesSchemaSql, PageTemplatesPluginColumnsSql (after PluginsSchemaSql) page_templates (+ plugin columns)
727 MenusSchemaSql menus, menu_items, status_appearance_entries, site_appearance_settings
1057 / 1104 / 1212 / 1264 IconMenuWrapSettingsSql, SiteConfigSiteInformationSql, SiteConfigSystemHealthSql (after PageTemplatesSeedSql), SiteConfigStatusAppearanceSql site-config menu seeds
1325 / 1441 PageTemplatesSeedSql, PageTemplatesThumbnailSeedSql built-in page_templates rows
1479 MenuItemsPluginColumnSql (after PluginsSchemaSql) menu_items.created_by_plugin_id
1502 / 1538 / 1571 / 1598 PluginsSchemaSql, PluginDataIsolationSql, PluginsIconMenuRemovalSql, PluginsSiteConfigMenuSql plugins (+ code, role_password_encrypted), role plg_readers, menu seeds
1660 / 1684 WorkflowExecutionErrorsSql, WorkflowTaskCompletionsSql workflow_execution_errors, workflow_task_completions
1701 / 1714 / 1753 SiteSettingsSql, NotificationsSql, LocalUserLockoutSql site_settings, notifications, local_users lockout columns
1770 / 1794 AuditOutboxSchemaSql, AuditOutboxDeadLettersSchemaSql audit_outbox, audit_outbox_dead_letters
1820 / 1876 SystemIssuesSchemaSql, SiteConfigSystemIssuesSql system_issues + menu seed
1953 / 2043 / 2262 SiteConfigFormsSql, SiteConfigChatbotSettingsSql, SiteConfigChatbotModelsMenuSql menu seeds
2113 FormsSchemaSql forms, form_versions
2165 ExternalConnectionsSchemaSql external_connection
2200 / 2444 AgentConversationsSchemaSql, AgentMessageSummaryColumnsSql agent_conversation, agent_message, agent_tool_call
2315 / 2346 / 2382 AgentModelCatalogSchemaSql, AgentModelCatalogSeedSql, AgentModelDefaultAvailableColumnsSql agent_model
2466 / 2632 / 2673 / 2705 ContentHierarchySchemaSql, ContentLocatorSchemaSql, ContentNotePageIndexSql, NotePreviewSvgSql projects, project_members, cabinets, notebooks, pages, page_versions, page_attachments, notes, note_versions, content_ancestors (+ locator columns)
2723 / 2873 ContentDocumentsSchemaSql, DocumentsMenuItemSeedSql folders, documents, document_versions, document_comments, document_bindings
2931 / 2950 / 2965 PageFavoritesSchemaSql, YjsDocumentsSchemaSql, ContentSampleProjectSeedSql page_favorites, yjs_documents, sample project
3020 DashboardsSchemaSql dashboards, dashboard_widgets, dashboard_shares
3092 / 3122 / 3169 SavedQueriesSchemaSql, QueryMenuSeedSql, DataMainMenuSeedSql (runs last) saved_queries + menu seeds
3242 ProjectionFrameworkSchemaSql projection_versions, projection_watermarks
3271 / 3379 / 3415 / 3430 WorkflowCacheSchemaSql, WorkflowEventLogSchemaSql, ProcessRetentionConfigSchemaSql, RecordActivityRollupSchemaSql workflow_execution_cache, workflow_task_cache, workflow_variable_cache, workflow_event_log_cache, process_retention_config, record_activity_rollup_cache
3454 / 3564 / 3611 DataStoresSchemaSql, DatasetsSchemaSql, SavedQueryShareTokensSchemaSql datastores, dataconnectors, datastore_files, datastore_tables, connector_runs, datasets, saved_query_share_tokens
3645 / 3719 PipelinesSchemaSql, CodeTransformersSchemaSql pipelines, pipeline_runs, pipeline_run_steps, code_transformers

Not owned here: local_users base table (infra/postgres/init/02-create-autonate-app-schema.sql), per-plugin plg_<code>.* (runtime Plugins/PluginSchemaProvisioner.cs + plugin migrations/*.sql), autonate_datastores schemas ds_<id> / cache_<id> (Services/DataStores/Sql/, Services/Datasets/Cached/).


3. src/AutoNate.Spa/src/ — the SPA

Config at project root: vite.config.ts (@src/, dev proxy for /api /account /dapr /bus-watcher /files + ws /ws/bus-watcher, /ws/agent-model-default to ASPNETCORE_URL ?? http://localhost:5108), tsconfig.app.json (strict), eslint.config.js, package.json scripts dev build type-check lint. No vitest, no Playwright JS config — browser tests are .NET (§8).

Path What lives there Rule for adding
main.tsx Provider order QueryClientProvider > SiteAppearanceProvider > MantineRoot > ModalsProvider > BrowserRouter > (Notifications, Router); imports @fortawesome/fontawesome-free/css/all.css, index.css, widgets.css Global providers only.
router.tsx AuthShell (login), full-bleed routes outside the shell (/documents/edit/:id, /documents/preview/:id, anonymous /q/:token), AppShellrenderAppRoutes() + path="*"DynamicPageRoute Only add a route here if it must render outside AppShell.
routes/appRoutes.tsx APP_ROUTES (:124) — parameterized routes and layout shells only; CONFIG_TEMPLATE_ANCHORS (:72) for admin/config/*; protect() wrapper; findCollidingAppRoute, anchorPathForTemplateKey used by the menu editor New parameterized page → entry in APP_ROUTES wrapped in protect(<X />); lazy-load heavy pages with lazy() + Suspense (pattern :18-36). New config sectionCONFIG_TEMPLATE_ANCHORS + PAGE_TEMPLATES key + pages/admin/config/sections.tsx export.
pageTemplates.tsx PAGE_TEMPLATES: Record<string, ReactElement> (:52-92); keys match page_templates.key; side-effect import "@/widgets" New menu-placeable pagepages/<area>/<Page>.tsx + key here + page_templates seed row (DatabaseSchemaInitializer.cs:1325) so an admin can place it on a menu.
pages/<area>/ One folder per feature: admin/ (Roles Groups Grants Hierarchy Explain Plugins Projections + config/ sections, datastores/ datasets/ dataconnectors/ pipelines/ code-transformers/), bus-watcher/, dashboard/, documents/, dynamic-page/, edge-types/, forms/, home/, login/, manage-users/, not-found/, notes/ (31 files), notifications/, query/, record-types/, records/, user-profile/, workflow/, workflow-executions/, workflow-tasks/ Page + its private sub-components, co-located .css, zod schemas (userSchemas.ts), and use<Page>PageContext.ts all live in the page folder.
api/<domain>.ts (44) + api/client.ts axios instance (withCredentials, rejects HTML on /api, 401 → /?returnUrl=); one module per backend prefix with const BASE = "/api/<x>" and (params, signal?) functions New API clientapi/<domain>.ts; never call axios directly from a page.
hooks/use<Domain>.ts (38) TanStack Query v5 hooks; key factories ["domain", "op", ...args] as const; useQuery({ queryFn: ({ signal }) => fn(params, signal) }); live invalidation via useBusSubscription, useInvalidateOnChannels Pair every api/<domain>.ts with hooks/use<Domain>.ts. Permission checks: usePermissionChecks + permissionKey (hooks/usePermissionChecks.ts).
types/ Shared DTO types (menus.ts, records.ts, flowable.ts, siteAppearance.ts, statusAppearance.ts) Cross-page DTOs only.
components/ Shared: data-table/DataTable.tsx (DataTableColumn<T>, wraps mantine-datatable), PageHeader.tsx, ConfirmModal.tsx, IconPicker.tsx, ColorPicker.tsx, AssigneePicker.tsx, SelectorBuilder.tsx, CronExpressionBuilder.tsx, JsxCodeEditor.tsx, JsxFormHost.tsx, SiteBrand.tsx; subfolders agent/ aql-editor/ documents/ notifications/ picker/ workflow/ Component used by >1 page → here (subfolder if >1 file). Tables go through DataTable; forms use useForm + zod4Resolver as zodResolver from mantine-form-zod-resolver with mode: "controlled"; tooltips use Mantine Tooltip; feedback via `notifications.show({ message, color: "green"
widgets/ Dashboard widget registry (registry.ts: registerWidget, WidgetDefinition<TConfig> with zod schema), index.ts (one import per widget), AutoConfigForm.tsx, dataSource.ts, DataSourcePicker.tsx, widget folders data-table/ mantine-chart/ quadrant-chart/ composite-chart/ New widgetwidgets/<name>/<Name>Widget.config.ts (schema + registerWidget), <Name>Widget.tsx, optional <Name>ConfigForm.tsx, + one import line in widgets/index.ts. CSS in widgets.css.
agent/ AgentSidebar.tsx (+ .css), AgentSidebarContext.tsx, AgentChatTrigger.tsx, ChatPaletteModal.tsx, api.ts, useAgentStream.ts, usePageKey.ts, pageContext/{PageContextRegistry.tsx,forms.ts,types.ts} Framework — do not edit per page. New page-context providerpages/<area>/use<Page>PageContext.ts calling useRegisterPageContext({ pageKey, getSnapshot, onPageQuery?, actions?, onPageAction? }); add the pageKey pattern to agent/usePageKey.ts; follow .claude/skills/add-page-context-provider.
shell/ AppShell.tsx (Mantine AppShell, header 56px, AgentSidebar sibling), AuthShell.tsx, NavMenu.tsx (menus main/icon/user), ProtectedRoute.tsx (auth-only), headerStyles.ts, shell.css Header chrome uses --app-* vars via headerStyles.ts.
lib/ Non-React utilities: siteAppearance.ts (applySiteAppearanceToDocument), statusAppearance.ts, faIcons.ts + fa-icons.json, blocknote/ (schemas, noteEmbedBlock), bpmn/, cron/, ws/ (subscription client), yjs/ (useYjsDocument, useBlockNoteWithYjs, YjsEditor, Excalidraw/draw.io hooks, ticket.ts) Pure helpers and editor integrations.
providers/, preferences/, menus/ MantineRoot.tsx (static theme), SiteAppearanceProvider.tsx; UserPreferencesContext.tsx + PreferencesModal.tsx; resolveItemPath.ts
index.css, widgets.css Global + surviving custom widget styles (.row-archived, .notification-unread, ManageUsers avatars) Prefer Mantine props; never re-add --bs-*, bi-*, form-control, panel-*.

4. src/AutoNate.Plugin.Abstractions/ — plugin SDK (namespace AutoNate.Plugins.Abstractions)

IAutoNatePlugin.cs, IPluginContext.cs, IHookRegistrar.cs / IActionHub.cs / IFilterHub.cs / HookHandle.cs / HookPoints.cs, IPluginMenus.cs, IPluginBehaviors.cs + IWorkflowBehavior.cs + BehaviorContext/Result/VariableValue.cs, IPluginProjections.cs, IPluginAgentSkills.cs, IPluginConnectors.cs + IPluginDataConnector.cs, IPluginTransformers.cs + IPluginTransformer.cs + DataFrame.cs, IPluginDataAccess.cs, AuditEventNotification.cs, AuthorizeFilterContext.cs, PluginDataRequest.cs. Rule: only JsonElement/primitives/these DTOs cross the ALC boundary; adding a member here is a contract change for every plugin — add the host impl + Noop* in src/AutoNate.Web/Plugins/ in the same commit, and keep PluginAssemblyLoadContext.SharedAssembliesplugins/Directory.Build.targets exclusions in lockstep.


5. services/

Path Contents Rule
services/hocuspocus/ src/{index,auth,persistence,webhook,materializers,noteEmbedStub}.ts, Dockerfile, README.md; env HOCUSPOCUS_PORT=1234, YJS_INTERNAL_SHARED_SECRET, AUTONATE_WEB_URL, Postgres New collaborative doc prefix → materializers.ts:283-305 switch + host YjsEndpoints.cs ticket mapping; fragment name must match the SPA hook.
services/executor/ src/{index,jsRunner,pythonRunner,pythonWorker,pythonProtocol,wire}.ts + test/*.test.mjs (npm test); NATS pipeline-code-run.> core queue subscriber (queue group executor) executor; isolated-vm (js) / pyodide (python) Wire format is Pipelines/Execution/CodeNodeWireFormat.cs on the host — change both.

6. plugins/<Name>/

Skeleton: <Name>.csproj (empty SDK project; plugins/Directory.Build.props supplies net10.0, <Private>false</Private> abstractions reference, and copies plugin.json, migrations/*.sql, PageTemplates/*.template|*.png to output), <Name>.cs : IAutoNatePlugin, plugin.json (name version entryAssembly [entryType] [templates]), migrations/NNN_*.sql (lexical order, tracked in plg_<code>.__plugin_migrations), PageTemplates/. Build → dist/<Name>.zip via plugins/Directory.Build.targets (AfterTargets="Build"); upload at /admin/plugins. Add the project to AutoNate.sln. Reference: plugins/Auditor/Auditor.cs:43-65 (action + data-view filter hooks). Never ship AutoNate.Plugin.Abstractions.dll, Npgsql.dll, Dapper.dll, or Microsoft.Extensions.*.Abstractions.dll in the zip. Follow .claude/skills/plugin-creator (note its IPluginContext member list is incomplete — see IPluginContext.cs).


7. flowable-extension/

pom.xml (Flowable 8, Spring Boot 4, Java 21), src/main/java/com/autonate/flowableevents/ (auto-configuration, two event listeners, DaprWorkflowEventPublisher, AutoNateBehaviorDelegate, DueDateHelper, script-task support), src/main/resources/META-INF/spring/…AutoConfiguration.imports, src/test/java/… (5 test classes). Built and tested inside infra/flowable/Dockerfile (mvn --batch-mode test package). Do not rename the autonateBehaviorDelegate bean; config keys are autonate.flowable-events.* (compose sets AUTONATE_FLOWABLE_EVENTS_*).


8. tests/ — where tests for each area go

Run: cd infra && docker compose -p infra up -d postgres nats nats-init redis then dotnet test AutoNate.sln (~8 min); E2E via make e2e.

Project Fixture Naming / placement
tests/AutoNate.Web.Tests/ (169 files) AutoNateWebApplicationFactory.cs (WebApplicationFactory<Program>, async CreateAsync(extraConfig), fresh autonate_test_<guid> DB per factory via PostgresTestDatabase.cs, dev auto-login as admin, authorization off, detectors/remediation/projection worker/retention off, AUTONATE_ALLOW_RUNNING_WITHOUT_DAPR=true; swaps StubFlowableClient, RecordingAuditEventPublisher, RecordingRecordEventPublisher) Endpoint tests <Area>EndpointsTests.cs (root); store tests EfCore<Thing>StoreTests.cs; event contracts <Domain>EventPublishingTests.cs; detectors <Detector>Tests.cs; skills <Name>SkillTests.cs; [Trait("Category","Integration")] on DB-touching classes. Subfolders: Authorization/ (44: *EnforcementTests.cs pass ["Authorization:Enabled"]="true", ["Authorization:Enforcement"]="full"; AuthorizationGatePresenceTests, SelectorParserTests, AuthorizerTests, ContentAuthorizerPolicyTests…), Query/ (AQL lexer/parser/endpoints), Hooks/, Plugins/ (PluginLoaderTests against the staged SamplePlugin, PluginDataIsolationTests), Workflow/, Datasets/, Storage/. New area with >3 files → its own subfolder + namespace AutoNate.Web.Tests.<Folder>.
tests/AutoNate.E2E.Tests/ (29 test classes) AutoNateE2EFixture.cs (collection fixture "AutoNate E2E"; boots AutoNate.Web as a child process with BuildSpa=true on a random port against a recreated AutoNate_E2E DB; Playwright chromium), Support/{E2ETestBase,ApiSeeder,ConsoleErrorGuard,TestNames}.cs One <Area>Tests.cs per feature (RecordsCrudTests, NotesTests, DocumentEditorTests, PipelinesAdminTests, AgentSidebarTests, PermissionGatingTests, …). This is the only SPA test surface — pipelines/transformers have no unit tests, only E2E. Plan/backlog: docs/playwright-test-*.md.
tests/AutoNate.Web.Tests.SamplePlugin/ SamplePlugin.cs, plugin.json; staged into AutoNate.Web.Tests/bin/.../test-plugins/SamplePlugin/ by the test csproj Extend when a plugin-host feature needs a real ALC round-trip.
flowable-extension/src/test/java/… JUnit 5 Java changes need a test here; runs inside the image build.

Area → test location quick map: endpoints/stores/events → AutoNate.Web.Tests/*Tests.cs; authorization/selectors/enforcement → AutoNate.Web.Tests/Authorization/; AQL → AutoNate.Web.Tests/Query/; hooks/plugins → AutoNate.Web.Tests/Hooks/, Plugins/; workflows → root Workflow*Tests.cs + Workflow/; projections → root ProjectionFramework*Tests.cs; system issues → root SystemIssue*Tests.cs + <Detector>Tests.cs; agent → root Agent*Tests.cs, *SkillTests.cs; content → root ContentTreeServiceTests.cs, Phase6/7*Tests.cs + Authorization/{Content,Document,Folder}*Tests.cs; data platform → root DataStore*Tests.cs, Datasets/; anything browser-visible → AutoNate.E2E.Tests/<Area>Tests.cs.

Clone this wiki locally