Making Meaning Explicit
The :Concept: Notation in ***plain Specifications
In our blog post Beyond Vibe Coding, we introduced the ***plain specification language and argued for a fundamental shift in software development: specifications should become the source of truth for software behavior, while source code becomes a secondary, ephemeral artifact. This shift changes what specifications are for. They are no longer informal guidance written primarily for humans, but authoritative inputs that must be interpreted reliably by automated systems.
A common objection to this approach is that natural language is inherently ambiguous. Source code, the argument goes, has a single, well-defined interpretation, while prose does not. If specifications are written in natural language, how could they ever function as an unambiguous source of truth for software behavior?
This post describes a syntactic feature of the ***plain specification language which addresses that concern. The :Concept: notation, together with a small set of accompanying rules, provides a disciplined way to define, reuse, and validate key concepts — without turning specifications into formal ontologies or rigid schemas. Where ambiguity would materially change the resulting software, it strengthens specifications by making critical meanings explicit and stable, while allowing natural language to remain expressive everywhere else.
Ambiguity in Practice
Let’s say a developer writes the following specification:
Write a task manager app.Without additional context, at least two interpretations of this specification are possible:
“Write a task manager app for an individual user that organizes concrete to-do items. Tasks should have priorities, deadlines, and a clear done/not-done state.”
“Write a task manager app that manages tasks as executable system processes. Tasks should be schedulable, observable, and capable of running concurrently.”
While the first interpretation is more probable in most contexts, the second interpretation is admissible and may be picked by an LLM when generating software code. If we want to avoid surprises during code generation and have specifications truly be the source of truth for the software’s functionality, we need to address such situations and reduce ambiguity in specifications.
Looking closely at this specification, we can see that a large part of its meaning hinges on the single word “task”. We can remove most of the ambiguity in the specification if we explicitly define the meaning of the word “task” as:
- Task describes an activity that needs to be done by the user.With this definition in place, the specification now strongly indicates that the developer’s intention is to describe a personal productivity application rather than a system for managing computational processes.
By explicitly defining the meaning of “task,” we have reduced ambiguity — but not eliminated it entirely. For example, the following interpretations are still both valid:
“Write a Node JS task manager web application for an individual user that organizes concrete to-do items. Tasks should have priorities, deadlines, and a clear done/not-done state.”
“Write a console task manager app in Python for an individual user that organizes concrete to-do items. Tasks should have priorities, deadlines, and a clear done/not-done state.”
We can address this other ambiguity in the specification in the same way, by explicitly defining the meaning of the word “app” as:
- App is a Python console application.But now imagine a longer specification containing dozens of words whose meanings materially affect the resulting software. How would a developer — or an automated tool — know which words have already been explicitly defined and which still carry implicit, potentially ambiguous meaning?
In text written for humans, a common way to draw attention to key concepts is to capitalize or bold them. However, overloading Markdown syntax1 for bold text — indicated using ** — with additional semantic meaning would be confusing for developers and fragile for automated tools
Instead, ***plain introduces a dedicated notation for concepts like “task” and “app” that need meaning explicitly defined. After evaluating several alternatives with respect to readability, ease of parsing, and low collision with natural language, we adopted a notation in which concept names are enclosed in colons at both the beginning and the end.
Using this convention, our running example becomes:
- :App: is a Python console application.
- :Task: describes an activity that needs to be done by the user.
Write :App: for managing :Task: items.Concept names must not contain spaces and may include only letters, digits, and a limited set of special characters such as dots and underscores2. This constraint ensures that concepts are easy to recognize programmatically while remaining readable to humans.
The goal of :Concept: notation is not to eliminate natural language or replace it with a formal ontology, but to provide a mechanism for anchoring meaning where ambiguity is costly. It is a precision tool rather than a requirement: words like “console,” “describes,” or “activity” may appear without special notation when the developer does not intend to constrain their interpretation.
Reducing Ambiguity with :Concept: Notation
:Concept: notation helps developers reduce ambiguity in specifications. Without explicit rules, however, concept annotations would risk becoming purely decorative, adding visual emphasis without enforceable semantic guarantees. To avoid this, and to ensure that explicit concepts can function as reliable anchors of meaning, ***plain defines a small set of rules that govern how concepts written in :Concept: notation are introduced and used. These rules are intentionally minimal. Their purpose is not to constrain how specifications are written in general, but to establish the conditions under which ambiguity around key concepts can be detected and addressed explicitly.
In ***plain, concepts written using :Concept: notation are governed by the following rules:
Concepts must be defined before they can be used.
Definitions must begin with a concept name.
Concepts must not be redefined.
Concept names are case sensitive.
Concepts must not change meaning through use.
Together, these rules establish the conditions under which concepts written in :Concept: notation can be reliably checked by automated tools and consistently used by software developers, making ambiguity in specifications easier to detect in practice.
A detailed discussion of these rules and the reasoning behind them is available in the whitepaper On the Concept of :Concept:.
From Incremental Prompts to Coherent Concepts
Consider a developer who already has an initial version of the task manager app and now wants to refine it based on user feedback. When using agentic AI development tools such as Claude Code, this refinement typically happens through a sequence of incremental prompts. For example, the developer might issue prompts like:
“Task must have a name.”
“Task has a due date”.
“Name is a required attribute.”
“Add notes to a task.”
“The name must be at least 3 characters long.”
“Due date is optional.”
“Name is a short description of the task.”
“Notes store additional details about the task.”
Each prompt is reasonable in isolation and, taken together, they fully describe the intended behavior of a task. However, neither the human developer nor the AI generating code ever sees a single, consolidated definition of what a task is in this system.
Instead, the meaning of “task” accumulates implicitly over time. Its structure, constraints, and semantics emerge indirectly from scattered instructions and are eventually encoded across constructors, validation logic, database schemas, and UI components. There is no authoritative place where the concept itself is defined. This is especially problematic given the limited context window of LLMs: when a concept is defined only implicitly, parts of its effective definition may fall outside the active context or receive insufficient attention during code generation.
For humans, implicit concept definitions increase cognitive load and fragility. When a concept has no explicit definition, its meaning must be reconstructed from scattered fragments across code and specifications, rather than consulted in a single authoritative place, making inconsistencies and accidental divergence more likely over time.
In ***plain, this pattern is replaced by an explicit, consolidated concept definition. A concept can be defined with as much precision as necessary, and that definition becomes the authoritative source of truth for its meaning.
Returning to our example, all of the requirements previously scattered across incremental prompts can be captured in a single definition of :Task:3
- :Task: describes an activity that needs to be done by the user.
- :Task: has the following attributes:
- Name - a short description of :Task:
- This is a required attribute.
- The name must be at least 3 characters long.
- Notes - additional details about :Task:
- Due Date - optional date by which to complete :Task:Rather than extending the concept implicitly through a sequence of prompts, this definition extends it explicitly and holistically. All essential properties, constraints, and relationships are visible in one place. This allows developers to reason about the concept as a whole, detect inconsistencies early, and evolve requirements without fragmenting meaning across prompts or code.
Explicit concept definitions also provide a much stronger semantic signal to automated tools. Instead of inferring structure and invariants from scattered instructions, an AI system can derive them directly from the concept definition itself. From the :Task: definition above, an implementation naturally follows, for example, a Python data class with clearly defined attributes.
Real software systems often contain dozens or even hundreds of such concepts, each with its own structure, constraints, and relationships. If these concepts are specified only implicitly through sequences of incremental prompts and represented only in generated code, the likelihood of misunderstanding grows steadily. Meaning becomes distributed, incomplete, and increasingly difficult to reconstruct, both for humans and for automated systems.
The :Concept: notation in ***plain addresses this problem directly. By giving concepts explicit names and dedicated definitions, it provides stable places where meaning can be accumulated intentionally rather than inferred indirectly. Concepts are not merely disambiguated, but progressively enriched, capturing structure, constraints, and relationships in a single authoritative form. As specifications grow, meaning becomes more precise rather than more fragile, remaining centralized in a single definition and avoiding semantic drift or silent forking across the system.
Explicit concept definitions also help coordinate understanding in larger software development projects, where specifications grow beyond what any single person can fully grasp. When concepts are defined only implicitly, different team members inevitably form different mental models based on the parts they work on. A single, authoritative definition provides a shared reference point, allowing teams to align on meaning, discuss changes precisely, and detect divergences early before they are encoded inconsistently into the software code4.
Conclusion
When specifications are treated as informal guidance, ambiguity is tolerable and often ignored. But when specifications become the source of truth for software behavior and the primary input to automated code generation, ambiguity becomes a liability. What was once a matter of interpretation turns into a source of silent and compounding divergence between intent, implementation, and outcome.
The :Concept: notation in ***plain is a pragmatic response to this shift. It does not attempt to formalize all meaning or replace natural language with rigid schemas. Instead, it provides a lightweight mechanism for anchoring meaning where precision matters most. By making key concepts explicit, stable, and reusable, it allows developers to commit to intent early, detect ambiguity where it is costly, and retain the expressive power of natural language everywhere else.
As specifications grow and evolve, explicit concepts offer a coherent place to accumulate understanding, reason about change, and integrate new requirements without fragmenting meaning across prompts, files, or generated code. For humans, they serve as shared reference points. For automated tools, they provide reliable semantic anchors that reduce guesswork and improve correctness.
Making meaning explicit is not an exercise in formalism. It is a recognition that when software is generated from specifications, meaning directly shapes the result — and must be handled with the same discipline as code.
*codeplain will soon release the first version of its platform based on the **plain specification language. If you want to be among the first to use :Concept: notation in practice, you can join the waitlist to get early access and updates.
***plain specification language uses Markdown as its base syntax.
See the whitepaper On the Concept of :Concept: for a definition of which ASCII characters are permitted in concept names.
A full example of the console task manager Python application is available at https://github.com/Codeplain-ai/console-task-manager
This use of explicit concept definitions is closely related to the idea of Ubiquitous Language from Domain-Driven Design, which emphasizes shared, precise terminology as a coordination mechanism among developers and domain experts.




