This file provides guidance to Claude Code and other coding agents when working in this repository.
artemis-feature-model is a standalone MVP for an interactive Artemis feature model. It turns a functional feature catalog into a constraint-aware exploration and configuration tool.
- Server: Spring Boot 4.0.x, Java 25
- Client: Angular 21, TypeScript 5.9, SCSS
- Build: Gradle wrapper, npm/Node 24
- Testing: JUnit via Spring Boot test support, Vitest for Angular tests
- Current styling baseline: Bootstrap-compatible SCSS
- Runtime model: classpath JSON resource
This MVP does not use a database, Liquibase, authentication, authorization, Helm, or the Artemis runtime.
- The backend exposes
GET /api/feature-model,POST /api/feature-model/validate,GET /api/feature-model/guided-workflow, safe read-only runtime provenance atGET /api/feature-model/provenance, deployment profile endpoints (GET /api/deployment-profiles,GET /api/deployment-profiles/{id}), profile-aware availability (GET /api/feature-model/profile-availability), Level 1 configuration artifact download (POST /api/feature-model/artifacts/download), and local runtime deployment package endpoints (POST /api/feature-model/deployment-package/previewand/download). - The Explorer route is read-only and supports tree/list inspection, filtering, expansion controls, and feature details.
- The Configurator route is a guided workflow. It supports use-case templates, guided decision cards, immediate validation, detailed violation and warning feedback, review summaries, a first-run tutorial, and an in-configurator tree view.
- The regular guided workflow should use user-facing outcome, recommendation, availability, and things-to-know text. Keep technical capability ids and artifact mappings in the advanced tree view.
- Deployment Profiles are JSON files; one bundled
default-artemis-profileprovides all capabilities and drives feature/option availability. The regular Configurator does not expose a profile selector; capability gating is a latent safety net for maintainer local overrides. Keep raw capability ids and missing-capability details in advanced tree/debug views. - Custom configuration starts from backend-derived
defaultSelectedFeatureIds; default-on guided options should be reflected as selected in the UI. - The in-configurator tree reflects guided selections in real time and can directly update the selection.
- Artifact and deployment-package generation are implemented. A valid selection
yields a downloadable ZIP: a Spring configuration overlay
(
application-feature-model.yml), an.env.example, selected-feature and deployment-profile metadata, and a generation report (Phase 5 Level 1); the deployment package adds local-checkout and self-contained remote-image Docker Compose stacks, helper scripts, a package manifest, and runtime checks for local validation. The review page generates and downloads directly; there is no preview step. Generation is DEMO-mode only and never writes plaintext secrets — secret values appear solely as${VARIABLE}placeholders. - The deployment package supports an export-time deployment-mode axis (D1+D2):
ArtifactGenerationRequesttakes an optionaldeploymentMode(stable string ids inDeploymentModes:local-docker,dev-ide,remote-ansible); an omitted mode selects local Docker without recording a deployment mode in the package manifest, while an explicitly chosen mode is recorded. Package generation is deterministic for the same active model, catalog, profile, and request. Profiles may declaresupportedDeploymentModes(absent = all; unknown entries warn, never fail loading), and an unsupported or unknown mode yields a controlled 400.DeploymentPackageServicecomposes packages per mode from shared artifacts; thedev-idemode emits the Level 1 overlay files plus a deterministic IntelliJ run configuration whoseACTIVE_PROFILESare derived from technical mapping tokens when present, with the original programming/hyperion rule retained only as the curated-model fallback, and a developer README. The profile order mirrors the run configurations Artemis ships and is semantic (buildagent must precede core, or the buildagent config excludes the JPA/DataSource auto-configuration and startup fails); an extrafeature-modelprofile makes Spring load the overlay directly once it is copied under its original name into the checkout's config directory, with the developer'sapplication-local.ymlkeeping final precedence. Afeature-model-demoprofile loads generated demo defaults for the overlay's${VARIABLE}placeholders — the dev-ide counterpart ofenv/.env.demo— so a DEMO run starts without manual environment setup; real environment variables override the dummies. The review page offers a deployment-target picker; the guided workflow itself has no deployment decisions. Artifact mappings only reach the overlay when they targetapplication-feature-model.yml. - The classpath model and generated snapshots now share the technical subtree
and drive both package modes.
dev-ideapplies the selected CI profile family while recording its developer-managed database choice and generating actionable MySQL or PostgreSQL instructions.local-dockergenerates an extends-based Compose stack for the selected database and CI provider plus a self-contained stack using the configuredghcr.io/ls1intum/artemisimage.start-demo.shuses the local checkout when given a path and the remote image without an argument. Runtime provenance is resolved strictly from active snapshot metadata or the classpath runtime properties;latestis rendered as a mutable tag and other values as digest references. Both stacks retain selection-consistency runtime checks. Its integrated-code-lifecycle stack points LocalVC at the containerized Artemis server and supplies the host Docker socket group through a platform-awareFM_DOCKER_GID. Jenkins configuration keys are mapping-driven and secret-safe. Its local Docker package also points LocalVC at the containerized Artemis server and declares the build-agent Git credentials that the production image does not supply throughapplication-localvc.yml. A local-docker Jenkins package deliberately has no Jenkins service yet, so it carries a prominent warning and a failingjenkins-stack-availablecheck. The classpath defaults are MySQL, integrated code lifecycle, and LocalVC. - The
remote-ansiblemode generates an admin-consumable Ansible deployment package: a self-contained mini values repository (pinned collectionrequirements.yml, minimalansible.cfgwithhash_behaviour = merge, playbook, inventory membership wiring and group values, preflight script) plus remote metadata (remote-readiness.json,vault-references.json). Values come from the curated Ansible binding catalog (src/main/resources/deployment-bindings/artemis-ansible-binding-catalog.json), an application resource in both source modes, versioned by its collection pin — never part of a model snapshot. Every selectable feature must be classifiedbound/no-op/unsupported; unclassified or inexpressible selections (module reduction, Jenkins) fail closed with a controlled 400 naming the catalog identity or missing variable. Secrets appear only aslookup('hashi_vault', …)expressions. The optional null-tolerantremoteEnvironmentrequest component supplies admin identity values; absent values yield a structurally identical placeholder package (environmentProvided: pending), and supplying the component on a non-remote mode is a controlled 400. Golden fixtures derived from the deploy-lab inventory pin the generated values byte-for-byte. The review page offers "Remote server (Ansible)" as a third deployment target sending onlydeploymentMode: "remote-ansible". - The generated overlay is statically validated against a curated Artemis config
key catalog (
src/main/resources/feature-model/artemis-config-key-catalog.json): unknown keys and value-type mismatches are reported without booting Artemis. The verdict ships asmetadata/static-config-validation.json, astatic-config-keysruntime check, and avalidate-package.shgate, and runs as a branch-scoped CI workflow. A drift-guard test fails when a model mapping path is missing from the catalog, so the catalog must be refreshed when Artemis config keys change. - Extraction runs as four composable commands plus one aggregate:
extractFeatureCandidates(the only one that reads Artemis sources),assembleFeatureModel,prepareGuidedWorkflow,packageFeatureModelSnapshot, andbuildFeatureModelSnapshot;featureModelManifestPreflightprints the derived source revision and manifest digest, andextractFeatureModelis a deprecated alias of the aggregate. Every command resolves the Artemis checkout, requires a clean git work tree, derives the run identity from checkout HEAD, and optionally verifies it against-PexpectedArtemisSha(the CI validation pin or a dispatch input). Each command owns one directory ofbuild/feature-extraction/<derived-revision>/{scan,model,workflow,report,snapshot}and consumes upstream artifacts only through digest-verified envelopes, so a stale or foreign intermediate artifact is rejected instead of composed. The model envelope covers both the generated feature model and generated config-key catalog; packaging never reads either as an unverified side file. Configuration enters throughFeatureExtractionInputs. Each command reads the manifest once — through the two-modefeatureManifestSourcestrategy (repositorycommitted default;checkoutreadssupportingFiles/feature-model/artemis-feature-manifest.ymlinside the checkout, and repository mode fails on a diverging co-located copy) — and binds its parsed content, byte digest, derived revision, and revision-scoped layout inExtractionRunContext; the local checkout resolves from-PartemisPath(or user-levelgradle.properties), thenARTEMIS_PATH, and no developer path is committed. Override the relocatable repository-mode manifest input with-PfeatureManifestPath=<manifest.yml>. Outputs are deterministic apart from scan-metadata timestamps; the drift report replaces the discovery step of the manual weekly consistency audit. - The scope manifest is schema version 3 and carries curation content only:
the source revision is derived from the verified checkout HEAD (a file
cannot pin the commit that contains it), the runtime image reference lives
in
delivery/artemis-runtime-image.json, and the retiredartemisCommitSha/artemisImageDigestfields are rejected with migration messages. The scan verifies before reading any file that the checkout is a clean git work tree and matches the expected revision when one is supplied. Curation is fail-closed — an undeclared candidate, a relation between included features without a declared constraint or anignoredRelationsentry, an orphan or ambiguous anchor, a colliding decision, or a failed extractor blocks the run, which writes diagnostics and exits non-zero without assembling a model. Manifest-authored semantics win over@ArtemisFeatureannotations, which only fill attributes the manifest leaves open and never grant membership. Exclusion reasons are optional: an omitted reason is normalized tounspecifiedand reported as a non-blocking warning. Missing rationale on an excluded runtime toggle also warns, while an included runtime toggle without rationale remains a blocking curation conflict. - The extraction run additionally assembles a complete generated feature model
from the manifest's include entries and conceptual nodes — including the
first technical subtree (
databasemysql/postgresql andci-providerintegrated-code-lifecycle/jenkins as maintainer-only xor groups plus the mandatorylocalvcbaseline, enforced throughalternativegroup relations andexcludesconstraints) — regenerates the Artemis config-key catalog from the scanned YAML defaults, validates model and bundled workflow through the shared loader/integrity/diagnostics code paths, and independently compares the emitted model with the resolved manifest contract, including membership, names/descriptions, kind/category/defaults, hierarchy and group semantics, required capabilities, artifact mappings, constraints, and relation ordering. Any mismatch is a blocking conformance finding in JSON and HTML. It also emits a complete deterministicsnapshot/folder. The manifest-driven generated model is canonical for delivery and is independent of the curated classpath development fixture. Runtime selects the complete classpath or snapshot bundle explicitly, andStaticConfigValidationServicealways consumes the catalog from that same validated bundle. - Enabled configuration keys are not standalone feature candidates and therefore need no manifest membership entries. Their constants, YAML defaults, and usage remain evidence on the owning module candidate and inputs to artifact mappings and the generated config-key catalog; new enabled-property anchors still surface through their module candidates and remain fail-closed.
- The authored
guided-workflow.jsonis lean: decision structure and teacher prose only. Model-owned wiring — optionrequiresCapabilitiesandartifactImpacts, the workflow's feature model pin, and review group members (now referenced bygroupNodeId) — is derived at serve time byGuidedWorkflowAssembler, so the served DTO shape is unchanged and capabilities are single-source on model features.GuidedWorkflowDiagnosticsServicesurfaces coverage, capability-validity, template-consistency, and stub-prose findings as logged warnings in the app and asguided-workflow-validation.json(with an automationstatus) in the extraction output; hard reference errors still fail hard../gradlew syncGuidedWorkflowScaffoldis a deliberate maintainer task that stubs newly included features with TODO prose, flags orphans without deleting, and leaves an already-covered workflow byte-identical. - Successful and controlled failed extraction runs write a dependency-free
report/index.htmlplus raw conformance, extraction, workflow, and release-delta JSON. Successful runs publish seven checksummed snapshot files: model, workflow, generated config-key catalog, generation report, deterministic provenance, metadata, andchecksums.txt../gradlew validateFeatureModelSnapshot -PsnapshotPath=<snapshot>validates the complete snapshot read-only; generation invokes the same validator before exposing a snapshot. Metadata validation is fail-closed for generated lifecycle status, extractor identity, and immutable image identity as well as payload names and cross-artifact provenance. - GitHub delivery reuses one read-only validation workflow for pull requests,
development-branch pushes, and publication. PR CI checks out Artemis at the
committed
delivery/artemis-validation-pinand validates deterministically; publication runs supply an explicitly resolved immutable SHA. Publication (publish-snapshot-image.yml,image-publishEnvironment) tags the content-addressed snapshot id, short-circuits on an already-published identity, attests build provenance, and records the registry digest. Promotion (promote-image.yml,image-promoteEnvironment) retags a published digest asverifiedwithout rebuilding and appends to thedelivery/verified-images.jsonledger via an automated PR; rollback is promotion of a prior ledger entry. The scheduled poller (poll-artemis-delivery.yml) resolves the Artemis tracking ref, publishes, and opens the delivery auto-PR (fixture refresh, validation-pin bump, coverage summary). No workflow publisheslatestor deploys the image; deployment remains deferred. The cutover to the Artemis-hosted manifest is documented indocs/extraction/automated-model-delivery.mdand not executed. - Runtime source mode is explicit under
artemis.feature-model.source-mode. Local development defaults toclasspath, which loads and validates the hand-maintained model, workflow, and config-key catalog as one bundle and rejects an active snapshot id. Production-like execution usessnapshot, requiresdata-rootplusactive-snapshot-id, validates the complete v2 snapshot during startup, and never falls back to classpath artifacts. The legacy/api/feature-model/snapshots/**administration resource is absent by default and can only be enabled explicitly in classpath mode. A controlled Docker named-context task stages one revalidated snapshot; the production image embeds it read-only, selects snapshot mode explicitly, runs as uid 10001 without a model volume, and records snapshot/source OCI labels.
Run commands from the repository root:
./gradlew test # server tests
./gradlew bootRun # server on http://localhost:8090
npm install # install client dependencies
npm run start # Angular dev server on http://localhost:9090
npm run build # Angular build into build/webapp
npm run test # Angular unit tests with VitestThe Angular dev server proxies /api/** to http://localhost:8090. The local ports deliberately avoid the Artemis dev defaults (8080/9000) so both applications can run side by side.
build.gradle
settings.gradle
gradle.properties
gradlew
gradlew.bat
package.json
package-lock.json
angular.json
tsconfig.json
tsconfig.app.json
tsconfig.spec.json
proxy.conf.json
src/main/java/de/tum/cit/aet/artemis/featuremodel/
src/main/resources/
src/main/webapp/
src/test/java/de/tum/cit/aet/artemis/featuremodel/
Server package areas:
catalogowns the feature model catalog, source model records, storage boundary, JSON-backed store, and model metadata.validationowns model and selection validation.visualizationowns derived tree/read-model structures.selectionowns user selection concepts and future selection sessions.snapshotowns the legacy, explicit-development-only local snapshot listing, import, and export surface.deploymentowns deployment profiles, profile loading, and capability resolution.exportowns Level 1 configuration artifact generation, the Level 2 local runtime deployment package, and static overlay validation against the Artemis config key catalog.extractionowns the manifest-driven staged pipeline.extraction.sourceowns upstream source conventions, verified location, and shared parse/scan results;extraction.domainowns persisted scan, envelope, and snapshot-bundle contracts;extraction.pipelineowns shared stage infrastructure — run context, verified input loading, manifest loading and preflight, the digest-verified artifact store, and controlled-failure reporting;extraction.scanowns the source scanners and candidate assembly behind the scan stage;extraction.modelowns curation, conformance, and model/catalog generation behind the model stage;extraction.workflowowns extraction-time workflow validation and scaffold authoring;extraction.reportowns report assembly and HTML rendering and depends only onextraction.domain;extraction.snapshotowns snapshot publication, validation, and Docker context staging behind the package stage;extraction.artifactowns deterministic JSON bytes, SHA-256 calculation, and artifact-directory lifecycle operations.sharedis only for truly shared exceptions, constants, and small utilities.
Client areas:
src/main/webapp/app/app.*contains the application shell and routes.src/main/webapp/app/feature-model/coreis for shared feature-model interfaces and pure helpers.src/main/webapp/app/feature-model/apiis for Angular API services.src/main/webapp/app/feature-model/exploreris for the read-only explorer route.src/main/webapp/app/feature-model/configuratoris for the interactive configurator route.guided/contains the guided workflow UI.guided/tutorial/contains the tutorial panel UI.tree/contains the in-configurator tree view.shared/contains configurator-local types and selection helpers.
src/main/webapp/app/feature-model/validationis for validation status and violation UI.
- Runtime resource:
src/main/resources/feature-model/functional-feature-model.json
Do not duplicate the feature model in client code. The server loads the runtime classpath copy and exposes it through APIs.
Tradeoff: These guidelines bias toward caution over speed. For trivial tasks, use judgment.
Don't assume. Don't hide confusion. Surface tradeoffs.
Before implementing:
- State your assumptions explicitly. If uncertain, ask.
- If multiple interpretations exist, present them - don't pick silently.
- If a simpler approach exists, say so. Push back when warranted.
- If something is unclear, stop. Name what's confusing. Ask.
Minimum code that solves the problem. Nothing speculative.
- No features beyond what was asked.
- No abstractions for single-use code.
- No "flexibility" or "configurability" that wasn't requested.
- No error handling for impossible scenarios.
- If you write 200 lines and it could be 50, rewrite it.
Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.
Touch only what you must. Clean up only your own mess.
When editing existing code:
- Don't "improve" adjacent code, comments, or formatting.
- Don't refactor things that aren't broken.
- Match existing style, even if you'd do it differently.
- If you notice unrelated dead code, mention it - don't delete it.
When your changes create orphans:
- Remove imports/variables/functions that YOUR changes made unused.
- Don't remove pre-existing dead code unless asked.
The test: Every changed line should trace directly to the user's request.
Define success criteria. Loop until verified.
Transform tasks into verifiable goals:
- "Add validation" → "Write tests for invalid inputs, then make them pass"
- "Fix the bug" → "Write a test that reproduces it, then make it pass"
- "Refactor X" → "Ensure tests pass before and after"
For multi-step tasks, state a brief plan:
1. [Step] → verify: [check]
2. [Step] → verify: [check]
3. [Step] → verify: [check]
Detailed project guidelines are maintained in docs/guidelines/. Treat those
files as the source of truth and avoid duplicating their contents here.
- Java Conventions
- TypeScript and Angular Conventions
- Client Styling and Theming
- API and Server Design Conventions
- Testing Guidelines
- Version Control Guidelines
When a change touches one of these areas, read the corresponding guideline
before editing code. Update the guideline document itself when a convention
changes; keep CLAUDE.md as the project overview and navigation entry point.