@specxai/moura
v0.2.1
Published
Git-native traceability and verification coverage CLI
Maintainers
Readme
Moura
Moura is an open-source, Git-native CLI for checking traceability between requirements, specifications, and test evidence, and for reporting verification coverage. It does not own the requirements or specifications: it connects the documents already reviewed in Git with evidence produced by test tools.
Status: Moura is in early development. Static project validation, Allure-backed evidence checking, and static Requirement Coverage reporting from the current project model and evidence are available.
First run
A Moura project contains moura.yaml plus the Requirement and Specification
Markdown files named by that manifest. The runnable
examples/vitest-minimal project is the
shortest complete onboarding path. Follow it from top to bottom to:
- confirm Node.js 24 or newer;
- install Moura as a local development dependency;
- create
moura.yaml; - write Requirement Markdown;
- write Specification Markdown;
- run
moura validate; - configure Vitest with the Allure reporter;
- add authoritative Moura metadata to a test;
- run the test and generate
allure-resultsJSON; - run
moura check --strict-traceability; - run
moura report; and - open
moura-report/index.html.
In an existing Node.js 24+ project, install the packages locally and validate the structure with:
npm install --save-dev @specxai/moura vitest allure-vitest allure-js-commons
npx moura validate .Tests expose the local IDs in a canonical Case path as separate Allure labels,
plus one moura_layer label:
await allure.label("moura_traceability", "managed");
await allure.label("moura_requirement", "REQ-001");
await allure.label("moura_scenario", "SCN-001");
await allure.label("moura_case", "CASE-001");
await allure.label("moura_layer", "unit");The example shows the direct Allure API and the presentation-only
epic/feature/story hierarchy in executable context.
After the tests have written allure-results/, run moura check. Success means there are no error-severity pairs and no malformed or semantically
invalid evidence. Explicit warning states remain visible but do not fail the check. moura check consumes existing evidence; it does not execute
the user's tests. See the check contract for details.
npm test
npx moura check . --strict-traceability
npx moura report .MISSING means a manifest-required Case × layer has no Evidence and is always
an error. UNMAPPED means a managed result lacks authoritative Moura mapping;
it warns by default and is an error under --strict-traceability.
Why Moura?
Teams often keep requirements, detailed behavior, and test results in separate formats. It can then be difficult to answer whether every specified case has evidence at every required verification layer. Moura aims to answer that question deterministically without introducing another system of record or requiring AI.
Its operating principles are:
- No server — analysis runs locally or in CI.
- No database — no separate traceability store must be operated.
- Git is the source of truth — reviewed files define the current specification.
- Tool-neutral core — evidence adapters translate external results; the core does not depend on Allure or another report format.
- Deterministic first — v0.1 focuses on static validation and aggregation. Optional AI-assisted gap suggestions may be added later.
Traceability model
Moura uses three domain roles:
Requirement
└ Scenario
└ CaseA local ID identifies a node among its siblings. The v0.1 convention is REQ-001, SCN-001, and CASE-001, but the model does not embed those prefixes or a three-digit rule. Local IDs must be non-empty and cannot contain /, Unicode whitespace, Unicode control code points (General_Category=Cc), or unpaired UTF-16 surrogates. Verification-layer names share the latter two interoperability restrictions; ordinary printable and supplementary Unicode remain supported.
Moura derives a logical canonical ID from the hierarchy:
REQ-001
REQ-001/SCN-001
REQ-001/SCN-001/CASE-001Canonical IDs are identities, not paths or prescribed filenames. Reparenting a node changes its canonical ID and therefore creates a different logical node.
The smallest coverage point is a canonical Case ID plus an open-ended verification-layer string, such as REQ-001/SCN-001/CASE-001 + integration. Tests and Cases have a many-to-many relationship; Moura does not require a proprietary test ID.
See Concepts, the v0.1 traceability specification, the initial evidence-checking contract, and the ID model for the authoritative details.
Traceability manifest
moura.yaml records relationships and required verification layers. Descriptions, boundary values, and expected behavior remain in requirement and specification documents rather than being duplicated into YAML.
version: 1
sources:
requirements:
- req.md
specifications:
- spec.md
verification:
layers:
- unit
- integration
requirements:
- id: REQ-001
scenarios:
- id: SCN-001
cases:
- id: CASE-001
verify:
- unit
- integration
- id: CASE-002
unimplemented:
- integrationverify requires Evidence; unimplemented explicitly records, in Git, that a Case × layer verification does not exist yet. Evidence absent from a verify pair is always MISSING—leaving it blank never implies UNIMPLEMENTED.
Canonical IDs are intentionally omitted and derived from the nesting. Layer names are strings rather than a closed enum, allowing domains to use values such as contract, security, manual, sil, or vehicle. The repository's own moura.yaml, req.md, and spec.md are the primary real-world example; see the v0.1 contract.
CLI
The npm package is @specxai/moura, and its installed executable remains
moura. From a project directory, the main commands are:
moura validate .
moura check .
moura report .moura validate [directory] # validate moura.yaml and its configured Markdown sources
moura check [directory] # check existing Allure evidence
moura report [directory] # write Quality Overview + Requirement Map
moura report [directory] --output <directory>All commands use the current working directory by default, or a supplied relative or absolute project directory. moura check first validates the project, then consumes existing evidence from <project>/allure-results/; it does not run tests or generate evidence. Every Case × verification-layer point must have success or warning severity, with no adapter or semantic evidence issues, for the check command to succeed. PASS is success; SKIPPED and UNIMPLEMENTED are warnings; FAIL, BROKEN, and MISSING are errors. moura report consumes the same structured result and produces deterministic static HTML without running tests.
Report CLI v2 makes moura-report/index.html the Quality Overview. Open its
Requirement Coverage link to reach the Requirement Map at moura/index.html.
The default project-relative output layout is:
moura-report/
├── index.html # Quality Overview
└── moura/
├── index.html # Requirement Map / Coverage
└── sources/ # escaped REQ/Spec HTML snapshotsmoura report .
moura report ./my-project --output reports/quality
moura report ./my-project --output /tmp/my-project-quality--output resolves relative to the project, not the caller's working directory.
Existing callers of reportProjectDirectory() keep outputPath for the Map;
overviewPath identifies the new top page. Rendering APIs remain available.
No repository scripts, development dependencies, hosting URL, or API key are
required by the installed package. Node.js 24 or later is required.
Overview uses the same authoritative evaluation as Moura's Quality Site. It
shows requirement coverage and missing Evidence, plus optional counts from
allure-results/*-result.json and lines from coverage/coverage-summary.json.
Absent or invalid optional metrics are unavailable. Missing Evidence inputs
still produce both pages with diagnostics; existing report exit-code behavior
is preserved. An artifact's creation does not mean verification passed.
Moura imports already generated HTML reports with optional flags:
moura report . \
--allure-report ./allure-report \
--coverage-report ./coverage \
--output ./quality-reportEither flag may be used alone. Paths resolve relative to the project root or may
be absolute. Reports with a non-empty index.html are bundled intact under
allure/ and coverage/, including CSS, JavaScript, images, fonts and data;
Overview links point to ./allure/ and ./coverage/. Relative links within each
report keep their original directory structure. Serve the output with a static
HTTP server when an external report requires fetch or other browser APIs.
Links outside the supplied report directory are not rewritten or bundled.
An existing report directory without a non-empty index.html is omitted, with
no detail link. Missing paths, non-directory inputs and unsafe assets are errors.
Moura neither runs tests or coverage measurement nor generates external HTML.
The HTML inputs never change metric sources or status aggregation.
Import only trusted reports: their HTML and scripts are published as supplied.
Input/output overlap and symlinks (including ancestor components) are rejected.
Only regular files and directories are accepted. Moura preserves filenames and
does not guarantee filename compatibility across operating systems. Unsupported
names or destination collisions fail through exclusive filesystem operations;
Moura does not rename, overwrite or skip assets. Across both inputs,
limits are 20,000 entries, 128 MiB per file, 512 MiB total and 64 directory levels.
Assets are copied into private staging before exclusive output publication;
copy failures clean up only this invocation's files. No output is deleted or
replaced. The repository Quality Site uses the same Report API, with SHA, source
links and validated Japanese presentation added for publication. Its _site/
output also must not already exist.
Use ?lang=ja or ?lang=en on any generated page. Titles use canonical English
unless an explicitly configured Japanese view passes the shared integrity
validator. To supply local views, create a JSON object such as:
{
"requirements.md": "docs/ja/requirements.md",
"specification.md": "docs/ja/specification.md"
}moura report . --japanese-views japanese-views.jsonBoth the JSON path and its mapped Markdown paths resolve relative to the project; absolute paths are accepted. Keys must match configured canonical source paths. Invalid translations fail before publication. Unmapped sources fall back to English. This validates traceability and protected Markdown, not translation meaning; review Japanese prose before distribution. No automatic translation or remote artifact retrieval occurs during CLI execution.
Reports are generated only into a destination that does not exist. Any existing
file or directory, even an empty directory or a previous Moura report, causes
an error containing the output path. Moura never deletes, overwrites, or updates
existing output. To regenerate, remove the destination yourself beforehand or
choose a new --output path. This applies to the default moura-report/ too.
Generation uses private staging and exclusive destination/file creation. Failed publication cleans up only items created by that invocation, preserving competing user data. New POSIX report permissions respect umask. Project/filesystem roots, protected inputs and symlink path components are rejected. No ownership metadata or backups are generated. Concurrent hostile filesystem mutation remains outside the local CLI's supported execution boundary. See migration notes.
Allure results associate evidence using one or more ordered moura_requirement, moura_scenario, and moura_case local-ID triples and exactly one moura_layer label. Moura reconstructs canonical Case IDs at the adapter boundary. See the Allure evidence adapter contract for supported statuses and input details.
For browsing, Moura also projects a canonical Case ID into Allure's standard
Behavior hierarchy: Requirement → epic, Scenario → feature, and Case →
story. These labels are presentation-only. The moura_* labels above remain
the authoritative evidence metadata consumed by Moura; the adapter never
reconstructs evidence from epic, feature, or story.
Development
Moura starts as a small Node.js 24+ and TypeScript project.
pnpm install
pnpm lint
pnpm lint:fix
pnpm format
pnpm format:check
pnpm typecheck
pnpm test # run the TypeScript test suite with Vitest
pnpm test:coverage # run tests and create coverage HTML/JSON/LCOV
pnpm test:allure # run the same suite and verify generated Allure results
pnpm test:onboarding # install and execute the public Vitest + Allure example
pnpm report:allure # create static Allure Report 3 HTML from allure-results
pnpm build && pnpm report:moura # create static Requirement Coverage HTMLpnpm test is the fast local test command and does not create persistent test
results. pnpm test:allure writes allure-results/ with the official Vitest
integration, then checks the emitted Moura metadata. Moura targets Allure Report
3+. Report generation remains a separate command so existing result validation
and static HTML generation have clear responsibilities. pnpm report:allure
loads allurerc.ts, which configures the Awesome report tree to group by
epic → feature → story, and verifies a representative generated tree.
The repository annotates real validation and checking tests to dogfood its declared verification contract. These declarations are reviewed mappings to test behavior; Moura never synthesizes passing evidence. See the dogfooding evidence mapping for the mapping rules and the current Case × layer inventory.
Evidence-producing tests use the Moura-owned custom Allure labels
moura_requirement, moura_scenario, moura_case, and moura_layer. They are
not built-in Allure identity or suite semantics: aligned hierarchy label values
map to canonical IDs in Evidence.covers[], while exactly one moura_layer
label maps to Evidence.layer. Case IDs and layer values must come from moura.yaml and obey
the same identifier/layer character contract. Invalid identity labels are
reported as evidence-input errors and do not produce normalized evidence.
Allure Report 3.17.0 builds each Behavior level from all values of a repeated
label. Repeating epic, feature, and story for a multi-case result therefore
creates a Cartesian product and loses the original Case tuple association. To
avoid displaying false paths, mouraEvidenceName() deterministically projects
only the first declared Case into the Behavior hierarchy. It still emits every
ordered Case tuple as authoritative moura_* metadata, so evidence checking is
unchanged and complete.
The executable exposes validate, check, report, version, and help commands. Domain types, coverage aggregation, report rendering, and canonical-ID construction are also exported for integrations.
Quality reports
Latest successful main branch reports:
Requirement Coverage rolls up authoritative pair results: only PASS is covered; every required layer must pass for a Case, every Case for a Scenario, and every Scenario for a Requirement. FAIL, BROKEN, SKIPPED, UNIMPLEMENTED, and MISSING remain visible gaps; warnings and errors are visually distinct. Moura verifies declared traceability and its evidence; it does not prove that a test semantically verifies the specification it declares.
Use 日本語 | English at the top of Requirement Coverage to select the display
language. The lang=ja or lang=en query parameter follows REQ and Spec links,
including links back to the report. Detail pages keep the same canonical IDs and
anchors in both languages; English remains the canonical source. The Quality site
adds Japanese translations only from the existing validated artifact. Missing,
invalid, or stale translations leave the English source readable, while retaining
the selected language for subsequent navigation. Standalone CLI reports also
support Japanese report labels and bundle the English REQ and Spec sources.
These static reports are produced by CI; pull requests retain their reports as workflow artifacts without replacing the public site.
License
Licensed under the existing Apache License 2.0.
