Practical questions about building with Vernac
You have a model compiling. Now you want to understand its generated API, connect it to an application, or explain an unexpected result. Start with the situation closest to yours below. These answers describe Vernac 0.5.0; source checkouts use matching 0.5.0-SNAPSHOT artifacts for local development.
New to the language? Follow your first domain model first. For exact requirements, use the specification.
For a guide by task across tutorials, contracts and implementation notes, open the documentation map.
On this page
- Understanding the approach
- Getting started and using Java
- I already have a Java/Spring application. What would Vernac replace?
- Do I need Spring, PostgreSQL and Docker just to try value objects?
- Can I create a complete application instead of starting with a domain test?
- Do I have to write a Java controller to use Thymeleaf with Vernac?
- The editor recognizes my model, but Maven cannot resolve Vernac. Why?
- Why does
value Title(String);exposestring()rather thanvalue()? - Why can an optional input be null while its getter returns Optional?
- Domain state and transactions
- My modify method failed validation. Why does the object still contain changes?
- A collection is immutable. Can I still change an entity inside it?
- Why does
duplicates()include the first occurrence as well? - Why does repository
byIdthrow when collectionbyreturns Optional? save()returned successfully. Is my change already committed?
- Persistence and migrations
- Events and integration boundaries
- If two listeners consume one event, do they share retries or acknowledgements?
- Does a database rollback undo an external API call? Can I simply retry a timeout?
- Why is an extra response field accepted, but a missing field fails?
- Can I generate an HTTP endpoint directly, and where does its transaction belong?
- Builds and generated files
- What happens to generated Java when I remove a Vernac definition?
- My build is green, but database tests were skipped. What have I actually tested?
- How can I show a numbered page without loading every matching aggregate?
- Does pagination make repeated requests observe one unchanged dataset?
- Can I return the lazy page iterator from a usecase to an HTTP serializer?
- When should I use a cursor instead of a numbered page?
- Why does my cursor stop working after a restart or on another server?
- Can one HTTP response contain two separately paged lists and other information?
- Why does the result require values rather than pages of aggregates?
- Do I need special pagination types in my domain model?
- How can an HTTP endpoint report a business failure without exposing internal details?
- Can I reuse values from a separately published Vernac library?
- Versioned modules in 0.4.0
- I changed a library VO. Why does my application need its own migration?
- Does adding a module dependency automatically create its database tables?
- Which module owns a stored event and which owns its delivery jobs?
- Will adding an API module dependency immediately expose its HTTP endpoints?
- I upgraded a module dependency. Why does the editor still show its old types?
- Where does Go To Declaration on an activated module take me?
- Can I explain what a domain word means without creating another Java class?
- Can I see a diagram without inspecting all generated Java classes?
- Debugging in Vernac
- Can I stop inside an inline Java behavior in a .vernac file?
- Why is debugger self a Read or Access view rather than my entity?
- Why does a source edit require a rebuild before debugging again?
- What does the yellow Log setting beside a breakpoint mean?
- Why can Evaluate Expression return a value while the variable remains red?
- Can I step from an emitting usecase straight into a deferred listener?
- Why does Ctrl+click navigate, but the type did not look like a link beforehand?
- Why does the new web application start but return 404 at
/?
Understanding the approach
Is Domain-Driven Design only worthwhile for large enterprise systems?
No. Its starting point is understanding the work and expressing its rules in a shared language. A small task board can benefit from a nonblank Title and an explicit CompleteTask operation. Large organizations also need strategic modeling, but a small application does not have to imitate their organization or infrastructure.
Vernac makes the repeated Java structure inexpensive: generated values, identities, usecase wrappers and repository implementations let you use clear boundaries without handwriting all the scaffolding. You still decide which boundaries help and test the rules. Start with What is DDD?, then DDD for a small project.
Must I use microservices, events and a port for everything?
No. DDD does not require microservices, and Vernac does not require a declaration of every supported kind. Start with one aggregate and a useful operation. Add a repository when you need storage, an adapter when you need a boundary and an event when you need an explicitly timed reaction. A plain Java method or direct usecase call can be the right choice. The small-project guide shows what to leave out as well as what to include.
Getting started and using Java
I already have a Java/Spring application. What would Vernac replace?
Vernac generates the repeated structure around your architectural decisions: typed
IDs, validated values, aggregate APIs, usecase transaction wrappers, repositories
and adapters. Your business behavior remains Java, either inline in a Vernac
behavior block or in a handwritten implementation class.
You can introduce a small domain model into an existing application. Handwritten Java can construct its values, call its operations and inject generated Spring beans. You do not have to replace every controller or rewrite the application. Follow one operation through the architecture.
Do I need Spring, PostgreSQL and Docker just to try value objects?
No. The basic template generates a Task model and runs an ordinary Java test. It needs neither a running Spring application nor a database. Repositories are explicit declarations; declaring an aggregate alone does not request persistence.
The core, JDBC, JSON and HTTP runtimes are separate modules. Add infrastructure as you need it. Start with the database-free example.
Can I create a complete application instead of starting with a domain test?
Yes, with the 0.5.0 plugin. New Project → Vernac → Spring Boot Web
Application creates a small task workspace with Thymeleaf, transactional Vernac
usecases, PostgreSQL, Flyway migrations and a local Compose file. With Docker
running, open TasksApplication and click Run, or run spring-boot:run in the
new project’s Maven window. Then open http://localhost:8080.
With no active profile, local is selected and starts the database. For deployment,
explicitly select production: this replaces the local default, expects external
DB_* configuration and never launches Docker. The supplied Dockerfile selects it
already. This is an intentionally public starter,
not a ready-made user/tenant system. CSRF protection is included; add authentication
before using private data. For a database-free introduction choose Minimal Domain
Project instead. The starter walkthrough
explains the files, tests, migration lifecycle and deployment boundaries.
Do I have to write a Java controller to use Thymeleaf with Vernac?
Not with the 0.5.0 MVC adapter. Declare adapter ... inbound mvc, explicit wire/form
bindings and a usecase call, then select render "template" model result or a local
redirect. Vernac generates the Spring controller and scoped error handling. You
still own the HTML/CSS and Spring Security configuration.
An invalid form can redisplay a named GET endpoint: its model is reloaded and
permitted original input values are restored, without retrying the failed POST.
Sensitive fields and headers are excluded; exceptions do not become public text
implicitly. See server-rendered pages. JSON APIs keep
using inbound http; the two forms share conversion and error selector rules.
The editor recognizes my model, but Maven cannot resolve Vernac. Why?
The IntelliJ plugin bundles its language server; it does not install the Maven
compiler/runtime artifacts. Check the application’s vernac.version, the Maven
runner’s JDK/settings and the selected Maven repository.
A released 0.5.0 consumer downloads matching artifacts from Maven Central after
publication. A source checkout using 0.5.0-SNAPSHOT requires a local installation:
run mvn clean install from the Vernac repository root with JDK 25. verify tests
and packages artifacts but does not install them. Restarting IntelliJ also discards
terminal-only environment settings. Check the plugin setup.
Why does value Title(String); expose string() rather than value()?
All ordinary fields follow the same type-based naming rule. String becomes
string, Title becomes title, and primitive int becomes intValue. A one-field
value object has no special naming rule, so adding a second field does not rename
the first getter.
Choose the API explicitly when another name is clearer:
value Title(String text);
This gives Title.of("Morning tour").text(). Use String value if you specifically
want value(). IDs are different: their fixed UUID accessor is always value(),
and the identity of an entity/aggregate is always id().
Explore default names and conflicts.
Why can an optional input be null while its getter returns Optional?
An optional field may be absent at construction. Factories accept @Nullable T
for that input, then expose Optional<T> through the getter so reading code handles
absence deliberately. Required inputs reject null.
For value Profile(Title, Title? nickname);, Java can write:
Profile profile = Profile.of(Title.of("Alex"));
String display = profile.nickname().orElse(profile.title()).string();
Use Optional.empty() for an optional behavior result; an Optional itself must
never be null. DTO and usecase-result records also expose Optional components,
while their of(...) factories accept nullable ordinary optional arguments.
Read the nullness rules.
Domain state and transactions
My modify method failed validation. Why does the object still contain changes?
A modify behavior changes the live object. Vernac checks invariants after successful
completion of the outermost synchronous modification; it currently has no in-memory
rollback. If behavior or validation throws, assignments already made may remain.
Stop the failed operation and decide how to recover. Do not save that graph as if it had been restored. A database transaction can roll back SQL, but cannot repair Java objects or undo an HTTP call. Candidate-state mutation is a documented proposal, not a current guarantee. Understand failure semantics.
A collection is immutable. Can I still change an entity inside it?
Yes. Immutability protects the container’s membership/order, not the state of an
entity it contains. stops.plus(stop) returns a new collection; it does not change
stops. Calling business behavior on an existing Stop can change that Stop.
This is why an entity collection cannot be a field of an immutable VO. Value/ID/enum collections can. Within a loaded aggregate, several relationships may refer to the same entity instance, so a change is visible through each of those references. Explore collection structure and identity.
Why does duplicates() include the first occurrence as well?
It answers “which original elements belong to a repeated equality group?”, rather
than “which later insertions were redundant?”. For Names.of(a, b, a), the result
is [a, a], in original order. This preserves all instances for investigation,
including entities with equal IDs but different state.
Use duplicates().distinct() for one representative per repeated group. A set or
distinct() retains the first equal instance; it does not decide which entity state
is newer. See the collection examples.
Why does repository byId throw when collection by returns Optional?
The operations express different expectations. repository.byId(id) requests an
aggregate expected to exist; absence raises AggregateNotFoundException.
stops.by(stopId) searches membership in an in-memory collection, where a missing
member is an ordinary result.
Declare a singleton repository query with find Tour? ... when absence is expected.
It returns Optional, but multiple matches still raise NonUniqueQueryResultException.
That declaration does not add a database uniqueness constraint or choose the first row.
Read the query contract.
save() returned successfully. Is my change already committed?
Not necessarily. A repository requires a surrounding transaction (MANDATORY).
A generated usecase starts or joins one (REQUIRED), and Spring commits after the
transactional method completes. A later exception or commit failure can still roll
back SQL written by save().
Call the usecase through its injected Spring bean. Constructing it with new, or
using self-invocation to bypass a Spring proxy, does not activate transaction
interception. Domain services coordinate rules but do not start their own transaction.
See the usecase contract.
Persistence and migrations
Why are there quoted names, dotted VO columns and @... columns in the SQL?
They encode the model without reserving extra domain words. The namespace becomes
the quoted PostgreSQL schema; each aggregate has its own root table. A single-value
VO is stored under its field name, while a multi-value VO expands to paths such as
"price.amount" and "price.currency". Technical columns use an @ prefix.
Child entities have aggregate-scoped state tables. Separate relationship tables
preserve shared references, and lists store @position. @version protects the
whole graph against stale writes. Read the generated schema rather than guessing
physical names. Follow a complete database round trip.
Do database constraints repeat my Java validates rules?
Vernac does not translate arbitrary validates expressions into SQL. Generated
constraints protect the storage representation: required data, references within
an aggregate, valid enum codes, list positions and coherent optional values.
For example, an absent nested VO must not leave partial data behind. A domain rule such as “this tour must fit its vehicle” still belongs in the model. Constraints are part of the schema model, so changes can produce a migration candidate; review its ordering and data implications before applying it. See structural constraints.
I reverted a migration experiment in Git. Why is the database still different?
Git restores files; Flyway has already changed the database and recorded that
migration’s version/checksum. Source, copied SQL under target/classes, and the
running database are three separate states.
First verify the actual datasource URL and database name. Connecting to the server’s
postgres database does not show the application’s tables or Flyway history. Then
choose a deliberate recovery: restore matching source history, use a forward
migration for shared data, or reset only a disposable demo database. Run Maven
clean to remove copied SQL that no longer exists in source.
Follow the Flyway workflow and recovery checklist.
What does schema-check establish before I deploy a migration?
It replays the committed Flyway SQL into one new disposable database, creates the expected schema in another, and compares Vernac-owned structures. It detects a migration chain whose resulting structure does not match the model; it ignores foreign application tables. It does not apply SQL to your application database.
Both databases start empty. Therefore it cannot establish that a backfill is correct
for historical data, locks are acceptable, or old/new application versions can run
together. Test those conditions separately and review every migration. force
merely allows candidate generation despite recognized data-loss risks; it approves
nothing and executes nothing. Understand migration tooling.
Events and integration boundaries
If two listeners consume one event, do they share retries or acknowledgements?
Each deferred listener has its own delivery job pointing to the same stored event occurrence. One listener’s success does not acknowledge another listener’s job. Workers use leases/fencing to coordinate concurrent delivery and recover from crashes.
Delivery is at least once, not an exactly-once guarantee for external effects. A handler can fail after an external system has accepted a request, so retries need an application-specific idempotency policy. Payloads use relational tables, not Java object serialization. Changing a nested VO is event evolution too: test whether stored events can still be reconstructed. Explore events and retries.
Does a database rollback undo an external API call? Can I simply retry a timeout?
No. PostgreSQL cannot roll back a carrier’s booking, filesystem write or email. A timeout also does not prove failure: the remote operation may have succeeded before its response was lost. Generated REST adapters do not retry automatically.
Use the typed failure category and responsibility information to decide who should investigate. Decide retry/idempotency at the application boundary according to the operation’s contract. Understand error translation.
Why is an extra response field accepted, but a missing field fails?
Generated DTO decoders tolerate unknown JSON properties, including nested ones, so a provider can extend a response without breaking consumers. Your declared required fields remain required: absence, null and wrong JSON types are errors.
An absent optional field differs from an empty list. PATCH adds a third distinction: unchanged, set, or clear. These are protocol decisions, not automatic aggregate updates. Try the DTO/PATCH tutorial.
Can I generate an HTTP endpoint directly, and where does its transaction belong?
Yes. Declare adapter ... inbound http, bind path/query/header/body values to an
existing usecase, and map the result to a local DTO. The controller performs transport
conversion; the usecase owns the transaction. Outbound port declarations describe
capabilities your domain calls, rather than APIs exposed to callers.
Malformed wire input defaults to 400, domain validation to 422, missing aggregates to 404, and optimistic conflicts to 409. Public error bodies contain a safe code and message. Response mapping happens after the usecase returns: if it fails, the usecase may already have committed. Do not automatically repeat a command because its HTTP response failed. Build an inbound endpoint.
Builds and generated files
What happens to generated Java when I remove a Vernac definition?
The compiler records ownership in a manifest and removes orphaned generated .java
files after successful generation. It does not delete arbitrary .class files or
JARs: Maven owns compiled output, and mvn clean clears it.
Keep handwritten code out of generated directories. After removing or renaming a type, use a clean build before interpreting stale compiled classes as current output. Read the output ownership contract.
My build is green, but database tests were skipped. What have I actually tested?
Only the tests that ran. Database-free domain, generated-Java and loopback HTTP checks are valuable, but cannot prove PostgreSQL behavior. Supply the documented disposable-server configuration, enable any example-specific profile, and inspect skipped counts before claiming database coverage.
The release workflow requires its named database and plugin suites to execute and rejects skipped reactor reports. Run the database tests and review the release gates.
How can I show a numbered page without loading every matching aggregate?
Declare a paginated repository query with a named list result and a complete
explicit order, for example find offset Issues open() order by createdAt desc, id asc;.
Its generated query object accepts page(OffsetRequest.ofPage(3, 20)); only that
window is returned, and every entry is a fully reconstructed aggregate. Existing
unqualified find methods keep returning their whole collection.
Add counting before the semicolon when the UI needs “page 3 of 27”. The returned
CountedOffsetPage exposes totalElements and totalPages. An offset past the last row
returns an empty items list with the original total, not an invented last page.
Without counting, OffsetPage provides hasNext without computing the total.
Does pagination make repeated requests observe one unchanged dataset?
No. A counted page’s root selection and count share one SQL statement snapshot, and child reconstruction verifies root versions. Concurrent changes to selected roots may raise an optimistic-lock conflict. Separate requests/traversal steps may observe new data, so inserts/deletes can shift offsets. Vernac does not silently change your transaction isolation or retry. Use stable sorting with explicit id as the final tie-breaker; cursor queries avoid offset shifting for unchanged sort keys, but do not freeze the dataset either.
Can I return the lazy page iterator from a usecase to an HTTP serializer?
No. Its next load needs the usecase transaction. Consume .pages(request).items()
inside that transaction, or load one materialized page and project its entries eagerly.
Every JDBC load checks the active transaction and rejects requests above the configured
maximum (vernac.pagination.max-page-size, default 1000). Pagination components in
Vernac Result declarations and HTTP mappings are supported. The
pagination tutorial shows the currently executable API.
When should I use a cursor instead of a numbered page?
Use find cursor for successive sections of an explicitly ordered list, such as an
activity feed or sequential processing. Start with CursorRequest.first(size), then
send the returned nextCursor with the same filters. The last returned root may be
deleted: its saved boundary still works. A cursor has no absolute page number or
count; choose find offset ... counting for “page 3 of 27”. Neither freezes future
requests against concurrent changes.
Why does my cursor stop working after a restart or on another server?
Without configuration, Vernac encrypts/authenticates cursors with a random process-local
key. Set vernac.pagination.cursor-key to the same secret (Base64 of 32 random bytes)
across restarts and replicas to retain continuations. Changing the key, filters,
repository method or owned schema invalidates old tokens; start a new traversal.
Never use tokens as authorization and never commit the key. See the
cursor configuration tutorial.
Can one HTTP response contain two separately paged lists and other information?
Yes. Declare a tuple result such as returns (Title heading, counted offset Summaries
openItems, counted offset Summaries closedItems, cursor Summaries activity). Each
component keeps its own request position and metadata. Project whole aggregate pages
into a named value list before Result.of. The adapter maps these into corresponding
counted offset list<ItemDto>/cursor list<ItemDto> fields; an entry mapping converts
the values while metadata is preserved. Each load is independent, rather than a
promise of one frozen snapshot across all board components. The
pagination tutorial shows the full model and GET.
Why does the result require values rather than pages of aggregates?
An aggregate is mutable domain state. Returning immutable summaries makes the application boundary explicit and lets serialization finish without accessing the repository. Use page.mapItems(Summaries::of, mapper), then declare the matching result variant. Sets lose order, optional pages confuse absence with an empty window, and lazy sequences would perform database work after the usecase transaction; all are rejected as result contracts.
Do I need special pagination types in my domain model?
No. Technical request factories live in the runtime Java API. You can define ordinary PageNumber/PageSize/CursorToken VOs with your own validation and pass them to a usecase. Unpack their getters explicitly into OffsetRequest/CursorRequest. Their spelling has no magic and does not reserve those names globally. The result uses a page qualifier, which is a completed application result, not a field type for a persisted aggregate.
All tutorials · Language reference
How can an HTTP endpoint report a business failure without exposing internal details?
Use an inbound errors policy. A status-only rule keeps the existing safe code and
message; two explicit strings provide an application-specific public code and message.
Rules on an endpoint run before shared adapter rules. Unmatched errors retain defaults.
Technical integration failures can be selected by their existing responsibility and
category: a remote timeout can become 503, while an invalid upstream response can
become 502. The HTTP reply does not erase the original integration diagnostics.
See inbound error policies.
Can I reuse values from a separately published Vernac library?
Yes. A type library publishes explicit exports through vernac-module.vmodule beside
its POM. Add its Maven dependency and import the exported namespace/type normally.
The consumer references the existing Java classes; it does not regenerate them.
External VO fields can be flattened into your own aggregate tables, so a library
update may require an application migration. Functional modules also support explicit
activation and coordinated, independent migration histories. See the two-artifact example.
Versioned modules in 0.4.0
I changed a library VO. Why does my application need its own migration?
A module owns only its own tables. If your application embeds an exported CustomerName in Account, its flattened columns belong to Account’s application history. Updating the module cannot migrate that other owner’s columns. Generate and review a migration for every owner embedding the changed value, then rebuild all dependent contracts. An added required field needs an explicit backfill decision for existing rows. Try the complete three-owner upgrade.
Does adding a module dependency automatically create its database tables?
No. Maven makes exported contracts available; type imports alone do not activate components or execute SQL. Explicit activation starts the selected capability. A usecase dependency can require another functional module transitively; the coordinator runs its history first. A mere dependency on a port or value does not trigger that activation. This keeps a reusable type library free from unwanted runtime side effects. Follow activation and ownership.
Which module owns a stored event and which owns its delivery jobs?
The producer owns the typed payload table and storage adapter. The application owns the one shared delivery table, with one job per deferred recipient. Activating a module receiver requires the producer’s storage; importing the event type alone does not start anything. The worker starts after required module histories and the application history have migrated and passed schema validation.
A failed receiver’s SQL rolls back before a retry is scheduled. Other recipients can succeed independently. External API effects still require idempotency. Run the restart/retry example.
Will adding an API module dependency immediately expose its HTTP endpoints?
No. A module can package an inbound HTTP adapter, but its routes and scoped error handler are registered only when that module is activated. Merely importing one of its values does not expose an API. This also holds when dependency packages are scanned.
Activate the API module in your .vapplication file. Its usecase dependencies require
other functional modules transitively, and their database migrations run first. The
application supplies the HTTP runtime and Spring MVC. The producer owns request/response
DTOs, mappings and the public error policy; none needs copying into the application.
Authentication and authorization still belong to your application’s security policy.
Try the module-owned API.
I upgraded a module dependency. Why does the editor still show its old types?
The language server deliberately uses Maven’s resolved dependency index instead of
resolving a second, possibly different version itself. Changing a dependency version
in the POM is therefore not enough: rebuild the consuming project with the matching
Vernac tools. The build writes target/vernac-module-index.json and source hints.
Editor/watch events and subsequent completion/navigation requests detect updated indexes.
An incompatible descriptor or required public contract produces a diagnostic, rather
than silently keeping stale types. Rebuild an older development module with the current
tools, or select a compatible dependency. Generated read-only views under target are
navigation aids; editing them cannot repair a published contract.
Where does Go To Declaration on an activated module take me?
An activation names a whole logical module, not one exported class. Navigation opens a read-only overview with its Maven coordinates, exports and runtime dependencies. This also works for listener-only or HTTP API modules with no exported types. To inspect an individual exported type, navigate its import/use instead: available source attachments are preferred, with a clearly marked contract view as fallback. Internal component names shown in the overview do not make those components public imports.
Can I explain what a domain word means without creating another Java class?
Yes. Declare a term in the relevant namespace: term Order { description "A submitted
purchase request."; }. A term may share a name with an aggregate or value object;
exact namespace/name equality associates the text with that definition. Quoted names
allow concepts with spaces and no corresponding class. Terms never become field types.
Use examples and notes to explain distinctions that a class name cannot convey. The IDE presents them in type hover, and a successful Vernac Maven compile writes readable and machine-readable glossaries under target. Exported module types carry their associated documentation so consuming Vernac editors do not need source attachments for that hover. Follow the vocabulary tutorial.
Can I see a diagram without inspecting all generated Java classes?
Yes. Select Tools → Vernac → Show Domain Diagram while editing a Vernac file, or run
mvn vernac:diagram. The generated Markdown page shows the selected namespace’s model,
its explicit relationships and associated vocabulary. Use Markdown Preview with the
Mermaid extension enabled. PlantUML source is also exported.
The diagram deliberately omits technical generated classes. Arrows describe declarations, not a reconstructed runtime call sequence. Run the action again after edits; automatic live diagrams and clickable graph navigation are outside this first increment. Follow the diagram tutorial.
Debugging in Vernac
Can I stop inside an inline Java behavior in a .vernac file?
Yes, with matching 0.5.0 compiler and plugin artifacts, or matching SNAPSHOT builds. Build the application
first, set a breakpoint on an executable statement or conversion in a supported
Vernac block and start its normal Java debug configuration. The editor can stop in the
Vernac source while the JVM supplies real arguments, locals and self. Breakpoints,
stepping, expression evaluation and conditions have been exercised in IntelliJ.
Native source mapping covers VO/entity/aggregate/collection behaviors, inline usecase execute blocks and their private helpers, plus domain service and outbound adapter operations and helpers. Java mapping bodies and explicit declarative field assignments in both adapter directions, inbound execute argument assignments and event require rules are mapped too. Listener execute bodies and private helpers are also mapped, preserving their actual execution stack. User-authored require rules on VOs, entities, aggregates, usecases and services have a separate rule-entry mapping: put the breakpoint on the opening require line, including for multiline conditions. Handwritten implementations use normal Java debugging. Try the complete walkthrough.
Why is debugger self a Read or Access view rather than my entity?
That is its real runtime type. Vernac deliberately passes a restricted facade to behavior or validation code. The debugger keeps that distinction visible instead of pretending that self is the owner object. The development renderer places recognized stored readable properties directly under the facade, without calling computed methods. Access means mutation through its methods; it does not make the projected field nodes writable. Optional children are labeled stored values and may be null, while the actual accessor still returns Optional.
The narrow stepping filter can skip pure facade field reads. It does not skip modification scopes, guards or require rules. For the technical object layout, enable Show Generated Java in Debugger before a fresh session. This disables both the presentation and the filter. See the exercises.
Why does a source edit require a rebuild before debugging again?
The loaded JVM class belongs to a particular generated implementation. Vernac checks source and generated-file hashes plus a fingerprint in the loaded companion class before mapping its lines back to the model. Otherwise an old program could appear to execute newly edited source. Rebuild and restart debugging after source changes; do not change the generated map by hand. This check also includes unsaved edits.
What does the yellow Log setting beside a breakpoint mean?
It is IntelliJ’s standard breakpoint logging UI. Log message records a breakpoint hit; Expression to log evaluates and prints a Java expression. With Suspend disabled, the breakpoint becomes a logpoint and execution continues. Right-click the breakpoint to inspect these settings. This is separate from application log levels and Vernac compiler warnings. See examples and expected results.
Why can Evaluate Expression return a value while the variable remains red?
The debugger runtime evaluator and its expression editor are separate mechanisms. Earlier development builds evaluated locals correctly while editor highlighting and Ctrl+Space lacked their Java scope. The current development integration maps both Java and source-file expression requests through the requested line’s Java context; this has now been confirmed for core paths in IntelliJ. Install matching compiler and plugin artifacts, rebuild the example and restart the session. If a new frame still shows red names, report editor resolution separately from successful runtime evaluation, including its source line and expression.
Can I step from an emitting usecase straight into a deferred listener?
A deferred listener executes later through the delivery worker, in its own transaction. It has a different runtime stack; the debugger must not pretend the original emitter is still its caller. Set a breakpoint in the deferred listener and resume the emitter. The worker can then reach that breakpoint when it processes the persisted delivery. Synchronous listeners instead run immediately during publication. The debugger tutorial distinguishes a small thread mapping probe from the full persistence-demo delivery exercise.
Why does Ctrl+click navigate, but the type did not look like a link beforehand?
Navigation commands and modifier-hover highlighting use different IDE entry points. Earlier Vernac builds supplied LSP navigation without native PSI references for DSL type ranges. The 0.5.0 plugin now supplies those precise ranges and compiler-resolved targets, so IntelliJ can provide its normal Ctrl-hover link feedback. This is separate from ordinary documentation hover. It follows your keymap/theme, and applies to resolved declared Vernac type usages, including imports and exported module types. Unknown/ambiguous names are not turned into guessed links. Injected Java keeps Java navigation. See the IDE tutorial.
Why does the new web application start but return 404 at /?
The application class can compile even if its generated MVC controller is missing.
A native IntelliJ build does not run Maven’s Vernac generation goal. Check whether
TaskWeb.java exists below target/generated-sources/vernac.
The 0.5.0 wizard enables Delegate IDE build/run actions to Maven for new
projects. In an older project or checkout, enable that option under Settings →
Build, Execution, Deployment → Build Tools → Maven → Runner, run clean compile
in the application’s Maven window and restart it. Keep its Run configuration’s
Build step enabled. This ensures later Vernac changes are generated before Run,
rather than requiring repeated manual compilation.