Skip to content

feat: base assistant detail page (#20)#68

Merged
martinyde merged 11 commits into
developfrom
feature/issue-20-assistant-details
Jun 17, 2026
Merged

feat: base assistant detail page (#20)#68
martinyde merged 11 commits into
developfrom
feature/issue-20-assistant-details

Conversation

@martinydeAI

@martinydeAI martinydeAI commented Jun 15, 2026

Copy link
Copy Markdown
Collaborator

Links to issues

  • Refs #20 — Assistant detail page
    (delivers the name + description + tags + model + framework
    half; remaining scope deferred — see the AI specificities block).
  • Refs #16.
  • Depends on PR #67
    (base Assistant entity), which depends on
    PR #59 (Doctrine +
    PHPUnit).

Description

Adds a base assistant detail page at GET /assistant/{id} plus the
fixture baseline the rest of the integration suite now reads from.

Application code

  • App\Controller\AssistantController::show — single action, route
    app_assistant_show. Symfony's MapEntity converter resolves
    {id}Assistant and 404s on a missing row.
  • templates/assistant/show.html.twig — extends base.html.twig,
    uses the existing Eyebrow / Box components, and reads from the
    assistant.detail.* translation keys added under the existing
    security pattern in translations/messages.da.yaml.
  • App\Controller\FrontpageController + templates/frontpage/index.html.twig
    — rail and stats blocks now consume real assistant data so the
    frontpage reflects the seeded catalogue (rail shows the five
    newest entries id-DESC; stats show total assistants and distinct
    language-model count).

Fixtures

  • App\DataFixtures\AssistantFixtures seeds 21 deterministic
    assistants
    : six hand-written rows (five authentic prototype
    entries plus the tagless Uden kategorier edge-case row used by
    the controller's empty-tags branch) and fifteen generated from a
    fixed grid of seven topics × ten kommuner × five language models.
    No randomness — same input every run.
  • tests/bootstrap_integration.php now loads AssistantFixtures
    alongside UserFixtures once per suite. DAMA's PHPUnit extension
    wraps each test in a transaction that rolls back at tearDown, so
    per-test mutations stay isolated while the baseline survives the
    whole run.

Tests

  • tests/Unit/DataFixtures/AssistantFixturesTest.php — pure
    TestCase mirroring UserFixturesTest; mocks ObjectManager,
    captures persist() calls, asserts the 6 + 15 layout, the
    tagless row's empty tags, unique (title, description)
    signatures for the generated entries, the full five-model
    rotation, and determinism across two load() invocations.
  • tests/Integration/Repository/AssistantRepositoryTest.php
    resolves the repo from the container and round-trips a
    persist / find.
  • tests/Integration/Controller/AssistantControllerTest.php
    three cases driven entirely off the fixture baseline (no ad-hoc
    new Assistant): happy path with tags
    (Borgerservice-vejviser), empty-tags branch via the
    Uden kategorier row, and 404 for an unknown id.
  • tests/Integration/Controller/FrontpageControllerTest.php
    fixture-only. Derives the expected top-5 rail entries via
    AssistantRepository::findBy([], ['id' => 'DESC'], 5) so the
    assertions stay correct if the seed grows, and pins the stats
    <dl> to the fixture totals (21 / 5).

Screenshot of the result

(Optional — screenshot the detail page and the updated rail/stats
block if useful for the reviewer.)

Checklist

  • My code is covered by test cases.
  • My code passes our test (all our tests).
  • My code passes our static analysis suite.
  • My code passes our continuous integration process.

If your code does not pass all the requirements on the checklist you have to add a comment explaining why this change
should be exempt from the list.

Additional comments or questions

do-not-merge is intentional — this PR sits on top of #67 (which
sits on top of #59). Per the CLAUDE.md do-not-merge rule the
blocker is the dependency chain; remove the label once both
ancestors land.


Details - AI specificities

Goal and motivation

  • Assistant details page #20 wants name, description, organisation/author, tags, model,
    parameters, system prompt, and an export entry point on a
    per-assistant detail page. The base entity from feat: add base Assistant entity (#14) #67 only carries
    six fields, so this PR delivers the half that has data today and
    defers the rest to follow-ups that each unblock a chunk.
  • Fixtures were folded into this PR because the frontpage rail and
    stats blocks needed real catalogue data to render meaningfully,
    and the integration tests for both controllers wanted a shared,
    deterministic baseline rather than ad-hoc new Assistant
    constructions in every setUp().

Scope

  • Controller + template for the detail page.
  • Frontpage controller and template, now reading from the
    repository.
  • AssistantFixtures seed (6 + 15 = 21 entries) with one tagless
    edge-case row.
  • tests/bootstrap_integration.php loads the assistant baseline
    alongside UserFixtures.
  • One unit test per new code surface (AssistantFixturesTest) and
    two integration tests refactored to read from the baseline
    (AssistantControllerTest, FrontpageControllerTest), plus
    AssistantRepositoryTest for the repository round-trip.

Non-goals / deferred

  • Export entry point → #22.
  • System prompt + parameters → #14
    (richer scope).
  • Organisation / author display → ADR 005 +
    #65.
  • Back-link to a real catalogue → #15;
    currently links back to the frontpage as a placeholder.
  • Empty-state branch of the rail. The fixture baseline is always
    loaded, so the no-assistants case is no longer reachable in the
    integration suite. If empty-state coverage matters later it
    belongs as a unit test of the rail template, not at the
    controller-HTTP layer.

Conventions applied

  • Fixture file pattern mirrors UserFixtures (registered as a
    service, loaded by tests/bootstrap_integration.php once per
    suite, isolated per-test by DAMA's enable_static_connection
    configuration in config/packages/dama_doctrine_test_bundle.yaml).
  • Unit-test pattern mirrors tests/Unit/DataFixtures/UserFixturesTest.php
    (pure TestCase, mocked ObjectManager, captures persist
    callbacks).
  • Integration controller tests are fixture-only — no
    new Assistant(...) / $em->persist / $em->flush. Test data
    is found by title (findOneBy(['title' => …])) so a fixture
    reshuffle that preserves titles doesn't break the tests.

Areas needing scrutiny

  • The "newest first" rail assertion in FrontpageControllerTest
    derives the expected ordering from the same query the controller
    uses (findBy([], ['id' => 'DESC'], 5)). That keeps it
    resilient to fixture growth but means the test won't catch a
    regression where the controller silently changes the order — a
    later snapshot-style test could add that.
  • Stats <dl> is keyed to 21 (total) and 5 (distinct language
    models). Reseeding AssistantFixtures requires updating this
    assertion; the test's comment names the five LMs to make the
    brittleness explicit.

Related

  • ADR 005 / #65 for
    the Organization entity that the detail page will eventually
    surface (author / domain).

Adds App\Controller\AssistantController with a single show action mapped at GET /assistant/{id}. Symfony's MapEntity converter resolves the route id to an Assistant (404 on unknown id). The Twig template renders the base fields the entity currently carries: title, description, framework + language model in a runtime box, and tags in a tag box when non-empty. Uses the existing Eyebrow / Box components and the Danish translation file (security pattern: keys under assistant.detail.*).

Out of scope per the entity's current shape: export entry point (#22), system prompt preview, parameters, organisation / author display, back-link to a real catalogue (#15) — back currently points at the frontpage. These hook in as the underlying data lands.

Tests: 38 cases, 100 assertions, 100% coverage. WebTestCase exercises the happy path with tags, the no-tags variant (asserts the tags section is omitted), and the 404 path. Depends on PR #67 (base Assistant entity) which depends on PR #59.

Refs #20, #16.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@martinydeAI martinydeAI added the do-not-merge Block merging until external dependency lands label Jun 15, 2026
martinydeAI and others added 4 commits June 15, 2026 08:45
Controllers stay thin per project conventions — the type signatures, attribute, and route name already describe what the action does. The descriptive blurb belonged on the (still-thin) service layer if anywhere; with no service yet, deleting reads cleaner than keeping placeholder docs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds an explicit paragraph under 'Controllers stay thin' so the convention demonstrated by stripping AssistantController's docblocks doesn't get re-added by a future session. The class name, route attribute, types, and return all carry the same information a class- or method-level docblock would — explanatory prose belongs on the service the controller delegates to.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
AssistantFixtures loads twenty assistants per doctrine:fixtures:load. Five are hand-written entries lifted from the AI Bibliotek prototype (Borgerservice-vejviser, Mødereferent, Journaliseringsassistent, Skole- og dagtilbudssvar, Tilsynsrapport-assistent) with detailed Danish descriptions and authentic kommune attribution in the description text. The remaining fifteen are generated from a fixed cartesian product of seven topics × ten kommuner × five language models, indexed by loop counter modulo each dimension — no randomness, so the same seed produces the same catalogue every run.

Per ADR 005 the kommune attribution is woven into the description for now; it migrates to the Organization relation when that lands.

Tests: 40 cases, 136 assertions, 100% coverage. Verifies the count, that the first five are the named authentic entries, that every generated row has a unique (title, description) signature and carries the kommune in the title, that the language model rotation reaches all five values, and that a second run on a fresh schema produces byte-for-byte identical titles.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
FrontpageController drops its SAMPLE_ASSISTANTS const and injects AssistantRepository. The rail iterates the five most-recently created Assistant rows (id DESC) and each card now links to /assistant/{id}. Stats: Assistanter and Sprogmodeller are computed via repository queries (count(\*), count distinct languageModel via a new repository helper); Kommuner stays a placeholder constant 10 until ADR 005 / #65's Organization entity lands. PHPDoc removed from the controller per the project convention.

Template: the existing CardRail:Card props (kommune / model / name / summary) are mapped to the Assistant entity's framework / languageModel / title / description for now; the kommune slot returns to a real organisation name once Organization lands.

Tests: 42 cases, 149 assertions, 100% coverage. FrontpageControllerTest now uses the schema-reset trait and covers three paths: empty DB still renders (with no card links), persisted assistants are linked by id and newest-first order is preserved, and the Assistanter + Sprogmodeller stats reflect the catalogue's actual count and distinct-LM cardinality.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Base automatically changed from feature/issue-14-assistant-base-entity to develop June 17, 2026 08:28
@martinyde martinyde removed the do-not-merge Block merging until external dependency lands label Jun 17, 2026
@martinyde martinyde requested a review from tuj June 17, 2026 10:40
tuj
tuj previously approved these changes Jun 17, 2026

@tuj tuj left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Rename kommune to municipality

Comment thread src/Controller/FrontpageController.php Outdated
@martinyde martinyde force-pushed the feature/issue-20-assistant-details branch from 1e4ad91 to 7c434cc Compare June 17, 2026 12:29
@martinyde martinyde merged commit f94be41 into develop Jun 17, 2026
7 checks passed
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