Skip to content
bitzorcas
中EN

Concept

Guidance contextual content and optional AI assistant

A source-verified overview of Markdown guidance, platform and tenant overrides, the mutable Draft/Published lifecycle, contextual selection, guided campaigns, AIManage session mappings, and NDJSON streaming boundaries.

Last updated

Guidance is the coordination module for in-application help content, guided campaigns, and AI-assisted sessions. It owns page/component Markdown guides, platform-default versus tenant-override selection, guided campaigns (Campaign) that organize published content into ordered multi-step guided paths, and mappings from Guidance sessions to AIManage conversations. AIManage still owns models, providers, usage, conversations, and generated messages.

1. Implemented behavior

  • unified GuidanceContent aggregate persistence with soft delete;
  • RouteKey, optional ComponentId, one RequiredRole, TenantId, language, content version, and Draft/Published state;
  • create, revise, publish, delete, list, detail, and contextual queries;
  • tenant-component, tenant-page, platform-component, platform-page ranking;
  • Published, role, and language filtering on contextual lookup;
  • unified tenant-owned GuidanceAssistantSession persistence;
  • stable SessionId derived from route/component/language;
  • a system prompt assembled from page, identity, roles, and the selected guide;
  • fail-closed assistant configuration and an explicit missing-workspace error;
  • synchronous messages and a handwritten NDJSON stream;
  • SqlSugar/EF Core parity foundations for content and sessions;
  • GuidanceCampaign guided campaigns: ordered multi-step, audience targeting (roles/users/whole-tenant), scheduling, a Draft/Active/Paused/Archived state machine, pre-assignment on activate with implicit assignment on first interaction, server-authoritative progress (idempotent completion and controlled dismissal), and completion-rate analytics.

The module does not currently provide immutable published revisions, history/diff/rollback/unpublish, publication approval, natural-key uniqueness, HTTP ETags, separate assistant permission/feature, prompt refresh/revocation, session TTL, start idempotency/compensation, a versioned stream protocol, a Markdown rendering-security contract, or complete HTTP security evidence.

2. Actual runtime shape

Authenticated client

7 content routes

3 assistant routes

11 campaign routes

guidance/content · guidance/campaign
View · Create · Update · Delete · Use

platform/tenant guards

GuidanceContentStore
GuidanceContent

GuidanceCampaignStore
GuidanceCampaign + Assignment

GuidanceContextBuilder
page + identity + roles + guide

GuidanceAssistantSession
GuidanceAiSession

AIManage
Conversation · Message · Model

Application sees only the Guidance-owned IAiSessionAdapter. The Infrastructure adapter still references AIManage Application directly and invokes IAIConversationStore plus message handlers. The old claim that Guidance has no direct AIManage dependency applies to Application/Content, not to the complete physical module.

3. HTTP routes

Method and routePurposeRegistration
POST /api/v1/guidance/contentscreate Draftgenerated
GET /api/v1/guidance/contentsadministration listgenerated
GET /api/v1/guidance/contents/{contentId}content detailgenerated
PUT /api/v1/guidance/contents/{contentId}revise same row and return to Draftgenerated
POST /api/v1/guidance/contents/{contentId}/publishtransition to Publishedgenerated
DELETE /api/v1/guidance/contents/{contentId}soft deletegenerated
GET /api/v1/guidance/contextualbest published guidegenerated
POST /api/v1/guidance/sessionscreate/reuse assistant sessiongenerated
POST /api/v1/guidance/sessions/{sessionId}/messagessynchronous messagegenerated
POST /api/v1/guidance/sessions/{sessionId}/messages/streamNDJSON streamhandwritten

A separate group of guided-campaign routes (create, list, detail, update, activate/pause/archive, analytics, campaigns visible to the current user, complete step, dismiss) sits under /api/v1/guidance/campaigns. For the full campaign route list, state machine, audience, and progress mechanisms, see Campaigns, assignment, and progress.

Nine non-streaming content/assistant routes are source-generated. The handwritten stream requires authentication and userPolicy, sets no-cache and disables proxy buffering, but calls DisableRequestTimeout without a dedicated maximum stream duration.

4. Create one tenant guide

Create a page-level English guide
// A regular tenant supplies its current tenant explicitly to avoid platform-null semantics.
var result = await sender.Send(new CreateGuide.Command(
RouteKey: "/billing/invoices",
ComponentId: null,
Title: "Invoice list operations",
BodyMarkdown: "1. Select a client.\n2. Verify invoice status.",
RequiredRole: "Attorney",
TenantId: currentTenantId,
LanguageCode: LanguageCode.EnUs), cancellationToken);
// Success creates a Draft; contextual readers cannot see it yet.
if (result.IsFailure)
return result.Error;
return result.Value!;

When a regular tenant omits TenantId, the command uses the current tenant. When the platform tenant omits it, the command creates platform content with TenantId null. The platform tenant can also create content for an explicit tenant. An unknown creation LanguageCode silently normalizes to zh-CN.

5. Contextual selection

yesnoyesnoyesno

Route + Component + Language
Tenant + Roles

undeleted Published only

tenant + component?

role-specific first
Version · ModifyTime · Id

tenant + page?

platform + component?

platform + page

RequiredRole is one string. It cannot express any/all roles, permissions, office, plan, feature, user attributes, or deny conditions. Duplicate candidates are legal in the database; the store chooses one through sorting rather than rejecting ambiguous natural keys.

6. Publication lifecycle

Create produces version 1 Draft. Publish mutates it to Published; repeated Publish returns Conflict. Revise updates title/body, increments version, and mutates the same row back to Draft. The live Published content disappears until another Publish. There is no history row or current-published pointer.

Delete loads and guards the aggregate before a global-ID soft delete. The store returns no affected count. A repeated delete normally becomes NotFound at the handler read.

7. Administrative read and end-user read are different security surfaces

Contextual correctly restricts Published, tenant/platform, role, and language. GetById first loads by global primary key, while EnsureCanRead permits TenantId-null platform content for every tenant and checks neither status nor RequiredRole. A user with generic content View who obtains a platform Draft ID may read its complete Markdown body.

List defaults IncludePlatform to true and does not filter RequiredRole, so it can expose platform Draft summaries. GA must separate end-user published-read from content-administration resources, permissions, and DTOs.

8. The assistant is not an automatic content fallback

Content APIs remain usable with AI disabled. Start Session itself fails when Guidance:EnableAiAssistant is false or the workspace is missing; it does not return a content-only session. Clients should fetch Contextual independently and start Assistant only when enabled.

All assistant requests use the guidance/assistant resource. The permission catalog now registers guidance.assistant.use. A missing identity becomes user ID "0" rather than failing closed.

9. Session reuse risks

SessionId contains the user ID plus a route/component/language hash, and lookups bind TenantId and UserId. This prevents straightforward cross-user access. Reuse nevertheless returns the existing AIManage conversation without updating SystemPrompt. A changed guide or revoked role can leave old restricted content in that conversation.

Concurrent Start requests can each create a conversation before competing on the session unique key. There is no explicit compensation between conversation creation and session insertion. If InitialQuestion fails, the session already exists while the command reports failure.

10. Reading path

11. Commercial GA red lines

  1. End-user APIs return only Published content allowed by the current audience.
  2. Draft administration permission is separate from ordinary content read.
  3. Published revisions are immutable; editing does not remove the live revision.
  4. Natural keys, published pointers, and concurrent updates have database/ETag guards.
  5. Markdown HTML, URL, image, CSP, and sanitizer policy is shared across clients.
  6. Assistant has separate permission, feature, quota, and a required user identity.
  7. Prompt data minimizes identity/roles and applies sensitive-content policy.
  8. Guide/role/permission changes refresh or revoke old sessions.
  9. Conversation/session creation is idempotent, atomic, or compensatable.
  10. NDJSON uses versioned frames, stable error codes, duration and size bounds.
  11. An AIManage owner contract enforces workspace, model, usage, and permissions.
  12. Dual-ORM, HTTP, concurrency, fault, privacy, and recovery evidence passes GA gates.

12. Source navigation

Terminal window
# Generated content and assistant routes plus one handwritten stream route.
rg -n "GenerateEndpoint\(|messages/stream|application/x-ndjson" \
src/Platform/Guidance src/Hosts/BitzOrcas.Api/Endpoints/GuidanceEndpointGroup.cs -g '*.cs'
# Campaign aggregate root, state machine, assignment and progress, permissions, and routes.
rg -n "GuidanceCampaign|guidance.campaign|CampaignResource" \
src/Platform/Guidance -g '*.cs'
# Current platform-read, identity fallback, unbounded timeout, and AIManage dependency.
rg -n "content.TenantId is null|UserId\?\.ToString|DisableRequestTimeout|AIManage.Application" \
src/Platform/Guidance src/Hosts/BitzOrcas.Api -g '*.cs' -g '*.csproj'
# Immutable publication and prompt version should have no matches.
rg -n "PublishedRevision|PromptHash|ExpiresAt" \
src/Platform/Guidance -g '*.cs'

Back to the module catalog · AIManage module · Authorization module

100%

Scroll or use controls to zoom · drag when enlarged · double-click for 100% / 200%