Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .agents/skills/openfasttrace/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,28 @@ Multiple coverage:

## Tracing

## Syntax: Gherkin

Gherkin `.feature` files can define OFT scenario items. Put exactly one OFT ID
in the contiguous tag region immediately before a `Scenario` or `Scenario
Outline`. Optional `# Covers:` and `# Needs:` comments belong between the tags
and the scenario header. Multiple `Covers` comments accumulate IDs; `Needs`
may appear once.

```gherkin
@id:scn~user-login~1
# Covers: req~authentication~1
# Needs: dsn, itest
Scenario: User logs in
Given a registered user
When valid credentials are entered
Then access is granted
```

Legacy coverage tags are recognized only in Gherkin comments, for example
`# [impl~login~1 -> dsn~authentication~1]`. Executable Gherkin lines are not
evaluated for coverage tags.

Tracing can be performed via CLI, Maven, or Gradle.

### CLI Usage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public interface ImportEventListener

/**
* The importer found the status of the specification item
*
*
* @param status
* the status
*/
Expand Down Expand Up @@ -94,15 +94,15 @@ public interface ImportEventListener

/**
* Add a tag
*
*
* @param tag
* the tag
*/
void addTag(String tag);

/**
* Set the location of the specification item in the imported file
*
*
* @param path
* the path of the imported file
* @param line
Expand All @@ -117,19 +117,40 @@ public interface ImportEventListener

/**
* Set the location of the specification item in the imported file
*
*
* @param location
* the location
*/
void setLocation(Location location);

/**
* Set to {@code true} if the specification item forwards needed
* coverage
*
* Set to {@code true} if the specification item forwards needed coverage
*
* @param forwards
* {@code true} if the specification item forwards needed
* coverage
*/
void setForwards(boolean forwards);

/**
* Add a complete specification item to the list of items being built. Use
* this method if the specification item is already complete and does not
* need to be built from events, e.g. when the item is specified in a single
* line and does not span multiple lines in the input file.
* <p>
* This is equivalent to the following sequence of method calls:
* <ul>
* <li>{@link #beginSpecificationItem()}</li>
* <li>{@link #setId(SpecificationItemId)}</li>
* <li>{@link #addCoveredId(SpecificationItemId)}</li>
* <li>{@link #addNeededArtifactType(String)}</li>
* <li>{@link #setLocation(String, int)}</li>
* <li>...</li>
* <li>{@link #endSpecificationItem()}</li>
* </ul>
*
* @param item
* the complete specification item
*/
void addSpecificationItem(final SpecificationItem item);
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ private SpecificationListBuilder(final FilterSettings filterSettings)

/**
* Creates a new {@link SpecificationListBuilder}.
*
*
* @return a new {@link SpecificationListBuilder}.
*/
public static SpecificationListBuilder create()
Expand All @@ -39,7 +39,7 @@ public static SpecificationListBuilder create()
/**
* Creates a new {@link SpecificationListBuilder} with the given
* {@link FilterSettings}.
*
*
* @param filterSettings
* the filter settings for the new builder.
* @return a new {@link SpecificationListBuilder}.
Expand Down Expand Up @@ -139,7 +139,7 @@ public void addTag(final String tag)
public List<SpecificationItem> build()
{
this.endSpecificationItem();
return Collections.unmodifiableList(this.items) ;
return Collections.unmodifiableList(this.items);
}

/**
Expand Down Expand Up @@ -177,14 +177,20 @@ public void endSpecificationItem()
{
final SpecificationItem item = createNewSpecificationItem();
// [impl->dsn~filtering-by-artifact-types-during-import~1]
if (isAccepted(item))
{
addNewItemToList(item);
}
addSpecificationItem(item);
}
resetState();
}

@Override
public void addSpecificationItem(final SpecificationItem item)
{
if (isAccepted(item))
{
addNewItemToList(item);
}
}

// [impl->dsn~cleaning-imported-multi-line-text-elements~1]
private SpecificationItem createNewSpecificationItem()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package org.itsallcode.openfasttrace.api.importer;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.*;
import static org.junit.jupiter.api.Assertions.assertAll;

import java.util.*;
Expand Down Expand Up @@ -57,6 +56,17 @@ void testBuildWithTags()
assertThat(items.get(0).getTags(), containsInAnyOrder("foo", "bar"));
}

@Test
void testAddSpecificationItem()
{
final SpecificationItem item = SpecificationItem.builder().id(ID).build();
final SpecificationListBuilder builder = SpecificationListBuilder.create();

builder.addSpecificationItem(item);

assertThat(builder.build(), contains(item));
}

// [utest->dsn~filtering-by-artifact-types-during-import~1]
@Test
void testFilterArtifactOfType()
Expand Down Expand Up @@ -153,18 +163,17 @@ void testDuplicateIdNotIgnored()
@Test
void testFilterSpecificationItemsByStatus()
{
final Set<ItemStatus> wantedStatuses = new HashSet<>();
wantedStatuses.add(ItemStatus.DRAFT);
final FilterSettings filterSettings = FilterSettings.builder() //
.wantedStatuses(wantedStatuses) //
final FilterSettings filterSettings = FilterSettings.builder()
.wantedStatuses(Set.of(ItemStatus.DRAFT))
.build();
final SpecificationListBuilder builder = SpecificationListBuilder
.createWithFilter(filterSettings);
addItemWithStatus(builder, "in-A", ItemStatus.DRAFT);
addItemWithStatus(builder, "out-B", ItemStatus.APPROVED);
addItemWithStatus(builder, "out-C", ItemStatus.PROPOSED);
addItemWithStatus(builder, "out-D", ItemStatus.REJECTED);
addItemWithStatus(builder, "out-E", null); // becomes APPROVED by default
// out-E becomes APPROVED by default
addItemWithStatus(builder, "out-E", null);
final List<SpecificationItem> items = builder.build();
assertThat(items.stream().map(SpecificationItem::getName).toList(),
containsInAnyOrder("in-A"));
Expand Down Expand Up @@ -255,7 +264,6 @@ void testMultilineTextFieldsGetTrimmed()
assertAll(
() -> assertThat(item.getComment(), equalTo("a comment")),
() -> assertThat(item.getDescription(), equalTo("a description")),
() -> assertThat(item.getRationale(), equalTo("a rationale"))
);
() -> assertThat(item.getRationale(), equalTo("a rationale")));
}
}
1 change: 1 addition & 0 deletions doc/changes/changes.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Changes

* [4.7.0](changes_4.7.0.md)
* [4.6.0](changes_4.6.0.md)
* [4.5.0](changes_4.5.0.md)
* [4.4.0](changes_4.4.0.md)
Expand Down
17 changes: 17 additions & 0 deletions doc/changes/changes_4.7.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# OpenFastTrace 4.7.0, released 2026-07-??

Code name: Gherkin Importer

## Summary

OpenFastTrace can now import traced specifications directly from Gherkin
`.feature` files. Annotate scenarios or scenario outlines with an OFT ID and
optional `Covers` and `Needs` metadata; existing coverage tags remain supported
when written in Gherkin comments.
Markdown and RST documentation can now also cover specification items with
coverage tags in their native comments.

## New Features

* #563: Added Gherkin `.feature` specification import for annotated scenarios and scenario outlines.
* #562: Added full and configured short coverage-tag import from Markdown and RST comments.
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# GH-562 Support Coverage Tags in Markdown and reStructuredText

## Goal

Allow Markdown (`.md`, `.markdown`) and reStructuredText (`.rst`)
documentation to define full and configured short OpenFastTrace coverage tags.
Documentation artifacts such as user guides, user manuals, and operator manuals
can therefore explicitly cover specification items while their dedicated
importers continue to parse ordinary specification items.

## Scope

In scope:

* Import existing full and configured short coverage tags from Markdown and RST
inputs.
* Support documentation artifacts such as user guides, user manuals, and
operator manuals as explicit coverage of specification items.
* Reuse the shared coverage-tag parser without changing its parsing semantics.
* Preserve the dedicated importer selection and existing markup parsing.
* Update traced specifications, user documentation, and the 4.7.0 changelog.

Out of scope:

* Changing importer priorities or routing Markdown/RST files through the Tag
Importer.
* Adding third-party dependencies or a public API.
* Multi-line or inline Markdown HTML comments, multi-line RST comments, and
RST directives. Coverage tags are supported only in single-line native
comments.

## Design References

* [System Requirements](../spec/system_requirements.md)
* [Design](../spec/design.md)
* [Quality Requirements](../spec/design/quality_requirements.md)
* [User Guide](../user_guide.md)

## Strategy

The recently introduced `CoverageTagParser` already contains the full- and
short-tag semantics and creates complete specification items through the atomic
`ImportEventListener.addSpecificationItem()` API. Add it to the shared
lightweight-markup importer and pass it the first matching `PathConfig` from
the dedicated Markdown or RST importer factory, using the same configuration
order as the Tag Importer. The common base owns parser construction, while each
format identifies its native, single-line comment candidates: Markdown forwards
only lines consisting of optional whitespace and a complete `<!-- ... -->`
comment; RST forwards only lines beginning with optional whitespace, `..`, and
whitespace that are not directives. Multi-line and inline HTML comments,
multi-line RST comments, and RST directives are not coverage-tag candidates.
Factories obtain configuration from their initialized
`ImporterContext`; tests that create the Markdown or RST factories must
initialize that context. The current markup state machine continues to process
every line. This keeps coverage-tag recognition comment-safe without duplicating
the shared parsing behavior or changing file-selection priorities.

## Task List

- [ ] Confirm the branch name follows the project convention:
`feature/562_support_coverage_tags_in_markdown_and_restructuredtext`.

### Requirements And Design

- [ ] Extend `feat~coverage-tag-import~1`,
`req~import.full-coverage-tag-format~1`, and
`req~import.short-coverage-tag-format~1` in
`doc/spec/system_requirements.md` so they cover supported documentation
files as well as source files.
- [ ] Add one Markdown and one RST acceptance scenario. Each scenario must
require import of full and configured short tags embedded in single-line
native comments as a documentation artifact at the tag-line location
while ordinary markup specification items remain unchanged; tag-shaped
non-comment text must not create coverage items.
- [ ] Add one runtime `dsn` item per scenario in `doc/spec/design.md`. Specify
delegation from lightweight-markup line processing to the shared parser
with the matching path configuration only for the defined single-line
native comment candidates; retain dedicated importer selection and
existing state-machine processing.
- [ ] Review the requirements and design changes before implementation.

### Implementation

- [ ] Add `openfasttrace-importer-tag-importer-common` as a direct dependency
of `openfasttrace-importer-lightweightmarkup` and add its JPMS module
requirement.
- [ ] In the Markdown and RST importer factories, select the first matching
`PathConfig` from `ImportSettings` in configured order, matching the Tag
Importer's precedence, and pass it to their importer instances. A missing
matching configuration must retain full-tag support and omit short-tag
parsing. Obtain the settings from the initialized `ImporterContext` and
initialize those factories in direct importer tests.
- [ ] In `AbstractLightWeightMarkupImporter`, create the shared parser with
the supplied `PathConfig` and provide a format-specific seam for deciding
whether a line is a coverage-tag comment candidate.
- [ ] In `MarkdownImporter`, forward only lines containing a complete,
single-line HTML comment (`<!-- ... -->`) apart from optional surrounding
whitespace; in
`RestructuredTextImporter`, forward only lines starting with optional
whitespace, `..`, and whitespace that do not use RST directive syntax.
Do not treat multiline or inline comments, or RST directives, as
candidates. Continue to run the existing markup state machine for all
input lines.
- [ ] Retain the existing full-tag behavior for generated IDs, explicit names
and revisions, multiple covered IDs, needed artifact types, source
locations, malformed tags, and listener events. Retain existing
configuration-driven short-tag semantics; do not expose a new API or
alter the Tag Importer.
- [ ] Add narrow `impl` coverage markers to the shared integration point for
both new design items.

### Verification

- [ ] Add Markdown and RST importer tests for a `doc` artifact covering a
requirement, including the tag-line location and unchanged ordinary item
parsing.
- [ ] Add negative tests proving tag-shaped text outside native Markdown or RST
comments is not forwarded to the tag parser and produces no coverage item.
Cover unsupported multi-line/inline Markdown and multi-line RST comment
forms, plus RST directives containing tag-shaped text.
- [ ] Add parameterized or focused tests for full-tag variants: multiple
covered IDs, explicit revision, explicit name and revision, needed
coverage, and malformed tags that produce no items.
- [ ] Add Markdown and RST factory/importer tests for matching and non-matching
`PathConfig` values, first-match precedence, configured short-tag import,
unchanged full-tag import when no configuration matches, and initialized
factory contexts in direct importer tests.
- [ ] Add product-level integration tests for `.md`, `.markdown`, and `.rst`
inputs to prove their dedicated importers produce the coverage items
ahead of the Tag Importer.
- [ ] Add `utest` and `itest` coverage markers at the narrowest applicable
test scope.
- [ ] Run focused module tests, `./oft-self-trace.sh`, and `mvn -T 1C verify`.

### Documentation And Changelog

- [ ] Extend `doc/user_guide.md` with comment-safe Markdown and RST examples,
such as `<!-- [doc->req~user-guide~1] -->` and
`.. [doc->req~user-guide~1]`, plus configured short-tag examples and the
applicable path-configuration requirement.
- [ ] Update `doc/changes/changes_4.7.0.md` with a GH-562 feature entry and
include the documentation coverage-tag capability in its summary.
Loading
Loading