Skip to content

Implement Phase 1: Clean Architecture foundation for canon-aware knowledge engine#1

Merged
JDRay42 merged 5 commits intomainfrom
copilot/implement-canonical-knowledge-engine
Dec 24, 2025
Merged

Implement Phase 1: Clean Architecture foundation for canon-aware knowledge engine#1
JDRay42 merged 5 commits intomainfrom
copilot/implement-canonical-knowledge-engine

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Dec 24, 2025

Bootstraps Palimpsest, a local-first authoring tool for managing large fictional universes with graph-first canon tracking, epistemic awareness, and provenance.

Architecture

Clean Architecture with strict dependency inversion:

  • Domain: 13 entities, 11 enums. Zero external dependencies.
  • Application: Repository/service interfaces. Defines contracts.
  • Infrastructure: EF Core, PostgreSQL with pgvector/pg_trgm, stub LLM providers.
  • Web: ASP.NET Core + HTMX + Shoelace. Server-rendered HTML.

Database Schema

Complete implementation per specification:

  • Canon graph: entities, assertions (append-only), edges (denormalized traversal)
  • Provenance: documents, document_versions, segments with stable locators
  • Epistemic tracking: Observed/Believed/Inferred categories on assertions
  • Review queue: questionable_items for conflicts, identity ambiguity, constraint violations
  • Context: dossiers (materialized bounded entity views), jobs (async pipeline tracking)
  • Vector search: segment_embeddings using pgvector for semantic retrieval

Indexes optimized for universe-scoped queries and graph traversal.

Core Concepts

Universe scoping: All operations execute within active universe boundary. Session-based context service enforces isolation.

Append-only assertions: S-P-O triples with typed objects (entity/literal/json), confidence scores, time scopes, and mandatory evidence segment references. No updates—conflicts flagged for author resolution.

Ingestion pipeline: Text → normalized → segments (chapter/section aware) → entities/mentions → assertions → edges. LLM integration stubbed for Phase 1.

Implementation

Repositories: Universe, Document, Entity, Assertion, Segment, Job. Async throughout.

Services:

  • UniverseContextService: Session-based active universe tracking
  • IngestionService: Text normalization, structural segmentation (chapter/section detection)
  • StubLLMProvider, StubEmbeddingService: Placeholders for Ollama/OpenRouter integration

UI: Universe CRUD with active universe indicator. Shoelace web components, HTMX for interactivity.

Docker

docker-compose.yml provisions PostgreSQL with pgvector extension. EF Core migrations ready.

Example Usage

// Universe-scoped repository access
var universeId = _universeContext.RequireActiveUniverseId();
var entities = await _entityRepository.GetByUniverseIdAsync(universeId);

// Append-only assertion creation
var assertion = new Assertion
{
    SubjectEntityId = entity.EntityId,
    Predicate = "birthday_day_month",
    ObjectKind = ObjectKind.Literal,
    ObjectLiteral = "10-07",
    Epistemic = EpistemicCategory.Observed,
    EvidenceSegmentId = segment.SegmentId,
    Confidence = 0.95f
};
await _assertionRepository.CreateAsync(assertion);

Deferred to Phase 2

  • Document upload UI and LLM-based extraction
  • Entity browsing and mention resolution
  • Conflict detection and dossier generation
  • Real LLM provider integration (Ollama/OpenRouter)
Original prompt

You are a senior software engineer acting as the primary implementer of an open-source, local-first authoring tool.

PROJECT OVERVIEW

You are building “Palimpsest”: a canon-aware knowledge engine for authors managing large fictional universes across many books and timelines.

The authoritative design is defined in the repository document titled 'spec' (primary specification)

These documents are ground truth. Do not invent features that contradict them. If anything is ambiguous or underspecified, stop and ask for clarification before proceeding.


TECHNICAL CONSTRAINTS (NON-NEGOTIABLE)

  • Language / Framework: C# on .NET (current LTS)
  • Frontend: Server-rendered HTML using HTMX and Shoelace
  • Database: PostgreSQL with pgvector and pg_trgm
  • Deployment: Docker Compose (local-first)
  • LLM Integration:
    • MVP support for local models (Ollama or equivalent)
    • Configurable support for OpenRouter
    • Provider-agnostic architecture
  • Embeddings: Local embedding computation for MVP
  • Authentication: Single-user, no login required (leave seams for future auth)
  • Scope Isolation: Hard universe boundaries; all operations occur within an active Universe context

ARCHITECTURAL PRINCIPLES

You must follow these principles strictly:

  1. Graph-first, not RAG-first

    • Entities, Assertions, Edges, and Conflicts are the system of record
    • Embeddings are retrieval helpers only
  2. Append-only canon

    • Never overwrite assertions
    • Conflicts are flagged, not auto-resolved
  3. Epistemic awareness

    • Every assertion is explicitly Observed, Believed, or Inferred
  4. Provenance is mandatory

    • Every assertion must point to an evidence segment with stable locators
  5. Universe as global mode

    • UI has a persistent Universe selector
    • All chat, ingestion, search, and commands operate within the active universe
  6. Bounded LLM context

    • Use entity dossiers and relevant segments
    • Never attempt to feed the entire universe into an LLM prompt

INITIAL RESPONSIBILITIES (PHASE 1)

Start with infrastructure and spine, not UI polish.

STEP 1: Repository Initialization

  • Create a clean solution structure:
    • /src/App (ASP.NET Core app)
    • /src/Domain (entities, assertions, predicates, enums)
    • /src/Infrastructure (Postgres, pgvector, repositories)
    • /src/LLM (provider interfaces and adapters)
    • /docs (do not modify specs unless explicitly instructed)
  • Establish database migrations tooling

STEP 2: Database Schema
Implement the schema exactly as defined in the specification, including:

  • universes
  • documents / document_versions
  • segments and segment_embeddings
  • entities, aliases, and mentions
  • assertions
  • edges
  • questionable_items
  • dossiers
  • jobs

Do not simplify the model “for now.” This schema is foundational.

STEP 3: Universe Context Plumbing

  • Implement a UniverseContext abstraction
  • Persist active universe selection (session or local storage)
  • Enforce universe scoping at repository and query boundaries

STEP 4: Ingestion Skeleton (No LLM Yet)

  • Accept .txt and .md uploads
  • Store raw and normalized text
  • Segment text into chapter/section/paragraph-style units
  • Persist segments with stable source locators
  • Create ingest jobs and progress tracking

STEP 5: Minimal Entity and Assertion Pipeline (Stubbed LLM)

  • Implement entity creation
  • Implement alias handling
  • Implement assertion persistence
  • Implement edge materialization
  • Stub LLM calls initially (mock assertions)
  • Ensure append-only behavior and provenance wiring

EXPLICITLY DEFER (DO NOT IMPLEMENT YET)

  • Full chat UX
  • Rich event modeling
  • Canon adjudication UI
  • Automated timeline solving
  • Advanced derivations beyond simple constraints
  • Multi-user authentication

If unsure whether something belongs in Phase 1, defer it.


DEVELOPMENT PROCESS EXPECTATIONS

  • Work incrementally
  • Prefer correctness and clarity over speed
  • Add TODOs where the spec implies later work
  • Write clean, readable code with explicit intent
  • Favor testability, even if tests are added later

When completing a meaningful milestone, summarize:

  • What was implemented
  • What assumptions were made
  • What is ready for review

MENTAL MODEL REMINDER

This system is not a chatbot with documents.
It is a canon compiler:

Documents → Segments
Segments → Assertions
Assertions → Graph
Graph → Dossiers
Dossiers → LLM grounding

If you lose this mental model, stop and realign.

Begin with schema and universe context, then proceed step by step.
Do not rush.


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI and others added 4 commits December 24, 2025 03:15
…nd Infrastructure layers

Co-authored-by: JDRay42 <10964706+JDRay42@users.noreply.github.com>
Co-authored-by: JDRay42 <10964706+JDRay42@users.noreply.github.com>
Co-authored-by: JDRay42 <10964706+JDRay42@users.noreply.github.com>
Co-authored-by: JDRay42 <10964706+JDRay42@users.noreply.github.com>
Copilot AI changed the title [WIP] Implement authoring tool for managing fictional universes Implement Phase 1: Clean Architecture foundation for canon-aware knowledge engine Dec 24, 2025
Copilot AI requested a review from JDRay42 December 24, 2025 03:28
@JDRay42 JDRay42 marked this pull request as ready for review December 24, 2025 17:05
Copilot AI review requested due to automatic review settings December 24, 2025 17:05
Copy link
Copy Markdown
Owner

@JDRay42 JDRay42 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initial shot by Sonnet 4.5 at following spec.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements Phase 1 of Palimpsest, establishing the foundational Clean Architecture for a canon-aware knowledge engine designed for managing fictional universes. The implementation focuses on bootstrapping the core infrastructure, universe management, and basic UI scaffolding while deferring LLM integration and advanced features to Phase 2.

Key changes:

  • Complete database schema implementation with EF Core, PostgreSQL, pgvector support
  • Universe CRUD operations with session-based context service
  • ASP.NET Core web application with HTMX + Shoelace UI
  • Stub services for LLM and embedding providers
  • Text ingestion pipeline with normalization and segmentation

Reviewed changes

Copilot reviewed 110 out of 149 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/Palimpsest.Web/Program.cs Service registration, EF Core configuration, repository and service dependency injection setup
src/Palimpsest.Web/Controllers/UniversesController.cs Universe CRUD controller with active universe context management
src/Palimpsest.Web/Views/Shared/_Layout.cshtml Base layout with Shoelace web components and HTMX integration
src/Palimpsest.Web/Views/Universes/*.cshtml Universe management UI (index, create views)
src/Palimpsest.Web/appsettings.json Application configuration including database connection string
src/Palimpsest.Web/Palimpsest.Web.csproj Web project configuration with EF Core Design tools
src/Palimpsest.Web/obj/* Build artifacts (should not be in version control)
src/Palimpsest.Infrastructure/obj/* Build artifacts (should not be in version control)
Files not reviewed (1)
  • src/Palimpsest.Infrastructure/Data/Migrations/20251224031829_InitialCreate.Designer.cs: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +65 to +66
@section Scripts {
<script src="https://unpkg.com/@@microsoft/signalr@latest/dist/browser/signalr.js"></script>
Copy link

Copilot AI Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SignalR script is included in the Create view but doesn't appear to be used. If SignalR functionality is not implemented yet, this script reference should be removed to avoid loading unnecessary dependencies.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +57
{
"version": 2,
"dgSpecHash": "J28we5vbHcA=",
"success": true,
"projectFilePath": "/home/runner/work/palimpsest/palimpsest/src/Palimpsest.Web/Palimpsest.Web.csproj",
"expectedPackageFiles": [
"/home/runner/.nuget/packages/humanizer.core/2.14.1/humanizer.core.2.14.1.nupkg.sha512",
"/home/runner/.nuget/packages/microsoft.aspnetcore.http/2.2.2/microsoft.aspnetcore.http.2.2.2.nupkg.sha512",
"/home/runner/.nuget/packages/microsoft.aspnetcore.http.abstractions/2.2.0/microsoft.aspnetcore.http.abstractions.2.2.0.nupkg.sha512",
"/home/runner/.nuget/packages/microsoft.aspnetcore.http.features/2.2.0/microsoft.aspnetcore.http.features.2.2.0.nupkg.sha512",
"/home/runner/.nuget/packages/microsoft.aspnetcore.webutilities/2.2.0/microsoft.aspnetcore.webutilities.2.2.0.nupkg.sha512",
"/home/runner/.nuget/packages/microsoft.bcl.asyncinterfaces/6.0.0/microsoft.bcl.asyncinterfaces.6.0.0.nupkg.sha512",
"/home/runner/.nuget/packages/microsoft.codeanalysis.analyzers/3.3.3/microsoft.codeanalysis.analyzers.3.3.3.nupkg.sha512",
"/home/runner/.nuget/packages/microsoft.codeanalysis.common/4.5.0/microsoft.codeanalysis.common.4.5.0.nupkg.sha512",
"/home/runner/.nuget/packages/microsoft.codeanalysis.csharp/4.5.0/microsoft.codeanalysis.csharp.4.5.0.nupkg.sha512",
"/home/runner/.nuget/packages/microsoft.codeanalysis.csharp.workspaces/4.5.0/microsoft.codeanalysis.csharp.workspaces.4.5.0.nupkg.sha512",
"/home/runner/.nuget/packages/microsoft.codeanalysis.workspaces.common/4.5.0/microsoft.codeanalysis.workspaces.common.4.5.0.nupkg.sha512",
"/home/runner/.nuget/packages/microsoft.entityframeworkcore/8.0.11/microsoft.entityframeworkcore.8.0.11.nupkg.sha512",
"/home/runner/.nuget/packages/microsoft.entityframeworkcore.abstractions/8.0.11/microsoft.entityframeworkcore.abstractions.8.0.11.nupkg.sha512",
"/home/runner/.nuget/packages/microsoft.entityframeworkcore.analyzers/8.0.11/microsoft.entityframeworkcore.analyzers.8.0.11.nupkg.sha512",
"/home/runner/.nuget/packages/microsoft.entityframeworkcore.design/8.0.11/microsoft.entityframeworkcore.design.8.0.11.nupkg.sha512",
"/home/runner/.nuget/packages/microsoft.entityframeworkcore.relational/8.0.11/microsoft.entityframeworkcore.relational.8.0.11.nupkg.sha512",
"/home/runner/.nuget/packages/microsoft.extensions.caching.abstractions/8.0.0/microsoft.extensions.caching.abstractions.8.0.0.nupkg.sha512",
"/home/runner/.nuget/packages/microsoft.extensions.caching.memory/8.0.1/microsoft.extensions.caching.memory.8.0.1.nupkg.sha512",
"/home/runner/.nuget/packages/microsoft.extensions.configuration.abstractions/8.0.0/microsoft.extensions.configuration.abstractions.8.0.0.nupkg.sha512",
"/home/runner/.nuget/packages/microsoft.extensions.dependencyinjection/8.0.1/microsoft.extensions.dependencyinjection.8.0.1.nupkg.sha512",
"/home/runner/.nuget/packages/microsoft.extensions.dependencyinjection.abstractions/8.0.2/microsoft.extensions.dependencyinjection.abstractions.8.0.2.nupkg.sha512",
"/home/runner/.nuget/packages/microsoft.extensions.dependencymodel/8.0.2/microsoft.extensions.dependencymodel.8.0.2.nupkg.sha512",
"/home/runner/.nuget/packages/microsoft.extensions.logging/8.0.1/microsoft.extensions.logging.8.0.1.nupkg.sha512",
"/home/runner/.nuget/packages/microsoft.extensions.logging.abstractions/8.0.2/microsoft.extensions.logging.abstractions.8.0.2.nupkg.sha512",
"/home/runner/.nuget/packages/microsoft.extensions.objectpool/2.2.0/microsoft.extensions.objectpool.2.2.0.nupkg.sha512",
"/home/runner/.nuget/packages/microsoft.extensions.options/8.0.2/microsoft.extensions.options.8.0.2.nupkg.sha512",
"/home/runner/.nuget/packages/microsoft.extensions.primitives/8.0.0/microsoft.extensions.primitives.8.0.0.nupkg.sha512",
"/home/runner/.nuget/packages/microsoft.net.http.headers/2.2.0/microsoft.net.http.headers.2.2.0.nupkg.sha512",
"/home/runner/.nuget/packages/mono.texttemplating/2.2.1/mono.texttemplating.2.2.1.nupkg.sha512",
"/home/runner/.nuget/packages/npgsql/8.0.6/npgsql.8.0.6.nupkg.sha512",
"/home/runner/.nuget/packages/npgsql.entityframeworkcore.postgresql/8.0.11/npgsql.entityframeworkcore.postgresql.8.0.11.nupkg.sha512",
"/home/runner/.nuget/packages/pgvector/0.3.0/pgvector.0.3.0.nupkg.sha512",
"/home/runner/.nuget/packages/pgvector.entityframeworkcore/0.2.1/pgvector.entityframeworkcore.0.2.1.nupkg.sha512",
"/home/runner/.nuget/packages/system.buffers/4.5.0/system.buffers.4.5.0.nupkg.sha512",
"/home/runner/.nuget/packages/system.codedom/4.4.0/system.codedom.4.4.0.nupkg.sha512",
"/home/runner/.nuget/packages/system.collections.immutable/6.0.0/system.collections.immutable.6.0.0.nupkg.sha512",
"/home/runner/.nuget/packages/system.composition/6.0.0/system.composition.6.0.0.nupkg.sha512",
"/home/runner/.nuget/packages/system.composition.attributedmodel/6.0.0/system.composition.attributedmodel.6.0.0.nupkg.sha512",
"/home/runner/.nuget/packages/system.composition.convention/6.0.0/system.composition.convention.6.0.0.nupkg.sha512",
"/home/runner/.nuget/packages/system.composition.hosting/6.0.0/system.composition.hosting.6.0.0.nupkg.sha512",
"/home/runner/.nuget/packages/system.composition.runtime/6.0.0/system.composition.runtime.6.0.0.nupkg.sha512",
"/home/runner/.nuget/packages/system.composition.typedparts/6.0.0/system.composition.typedparts.6.0.0.nupkg.sha512",
"/home/runner/.nuget/packages/system.io.pipelines/6.0.3/system.io.pipelines.6.0.3.nupkg.sha512",
"/home/runner/.nuget/packages/system.reflection.metadata/6.0.1/system.reflection.metadata.6.0.1.nupkg.sha512",
"/home/runner/.nuget/packages/system.runtime.compilerservices.unsafe/6.0.0/system.runtime.compilerservices.unsafe.6.0.0.nupkg.sha512",
"/home/runner/.nuget/packages/system.text.encoding.codepages/6.0.0/system.text.encoding.codepages.6.0.0.nupkg.sha512",
"/home/runner/.nuget/packages/system.text.encodings.web/4.5.0/system.text.encodings.web.4.5.0.nupkg.sha512",
"/home/runner/.nuget/packages/system.threading.channels/6.0.0/system.threading.channels.6.0.0.nupkg.sha512"
],
"logs": []
} No newline at end of file
Copy link

Copilot AI Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Build artifacts from the obj/ directories should not be committed to version control. These files are auto-generated during the build process and should be excluded via .gitignore. Committing these files can lead to merge conflicts and repository bloat.

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,13 @@
{
"ConnectionStrings": {
"DefaultConnection": "Host=localhost;Port=5432;Database=palimpsest;Username=palimpsest;Password=palimpsest_dev"
Copy link

Copilot AI Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The database connection string contains hardcoded credentials (username: palimpsest, password: palimpsest_dev). For production or shared development environments, credentials should be managed through environment variables, user secrets, or a secure configuration provider to prevent accidental exposure of sensitive information.

Copilot uses AI. Check for mistakes.
Comment on lines +7 to +9
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@@shoelace-style/shoelace@2.12.0/cdn/themes/light.css" />
<script type="module" src="https://cdn.jsdelivr.net/npm/@@shoelace-style/shoelace@2.12.0/cdn/shoelace-autoloader.js"></script>
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
Copy link

Copilot AI Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Frontend dependencies are loaded from CDN (Shoelace CSS/JS and HTMX). For a local-first application as described in the PR, consider bundling these assets locally to ensure the application works offline and to have better control over versions and security updates.

Copilot uses AI. Check for mistakes.
Comment on lines +99 to +127
foreach (var paragraph in paragraphs)
{
if (string.IsNullOrWhiteSpace(paragraph))
continue;

var trimmed = paragraph.Trim();

// Try to detect chapter headers
string? chapterLabel = null;
if (IsChapterHeader(trimmed))
{
chapterLabel = trimmed;
}

var segment = new Segment
{
SegmentId = Guid.NewGuid(),
VersionId = versionId,
ChapterLabel = chapterLabel,
SectionPath = null, // TODO: Build section path from document structure
Ordinal = ordinal++,
Text = trimmed,
SourceLocator = $"{{\"offset\": {currentOffset}, \"length\": {trimmed.Length}}}",
CreatedAt = DateTime.UtcNow
};

segments.Add(segment);
currentOffset += paragraph.Length + 2; // Account for paragraph separator
}
Copy link

Copilot AI Dec 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This foreach loop implicitly filters its target sequence - consider filtering the sequence explicitly using '.Where(...)'.

Copilot uses AI. Check for mistakes.
@JDRay42 JDRay42 merged commit 62e16db into main Dec 24, 2025
6 checks passed
@JDRay42 JDRay42 deleted the copilot/implement-canonical-knowledge-engine branch January 7, 2026 21:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants