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
118 changes: 118 additions & 0 deletions .agents/skills/requirements-management-skill/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
---
name: requirements-management-skill
description: >-
Requirements traceability for S-CORE baselibs Sphinx-Needs. Use when authoring or reviewing comp_req/aou_req requirements, looking up external feat_req/stkh_req needs, validating version-pinned links and safety consistency. Use also when authoring/reviewing/migrating C++ and Rust unit tests as those always need comp_req links and verification metadata.
---

# Requirements Management Skill

## Routes

### Author or edit `comp_req` / `aou_req`

1. Edit `docs/baselibs/components/<component>/docs/requirements/index.rst`.
2. Use a stable lower-snake-case ID: `comp_req__<component>__<short_name>` or `aou_req__<component>__<short_name>`.
3. Set every required attribute from [Requirement Attributes](#requirement-attributes), including `:version:`.
4. For `comp_req`, pin every `:derived_from:`, `:satisfied_by:`, and `:covers:` target as `<target_id>[version==N]`.
5. Check the linked target exists and the pinned version matches the authoritative need data.
6. Validate with `bazel run //:docs`; the route is complete when the docs build has no metamodel, link, or schema errors caused by the change.

### Look up external `feat_req` / `stkh_req`

1. Do not grep for external feature or stakeholder requirement sources in this repo; they are imported from `score_platform` needs JSON.
2. Locate the Bazel output base with `bazel info output_base`.
3. Read the relevant `*score_platform+/needs_json/.../needs.json` file for `id`, `title`, `content`, `status`, `version`, and links.
4. For process-level needs such as `wp__requirements_comp`, read the parallel `*score_process+/needs_json/.../needs.json`.
5. The route is complete when each referenced need ID, version, safety level, and upstream trace is accounted for.

### Link tests to requirements

1. Read [`test-to-requirement-linking.md`](test-to-requirement-linking.md) before authoring, reviewing, or migrating test metadata.
2. Link C++ gtest and Rust unit tests to the `comp_req__...` they verify using the mandated metadata: `PartiallyVerifies` / `FullyVerifies`, `TestType`, `DerivationTechnique`, and `Description`.
3. The route is complete when every affected test has correct metadata and the requirement it verifies exists.

## Requirement Types, Traceability & Naming

Traceability runs `stkh_req --derived_from-> feat_req --derived_from-> comp_req`. `stkh_req` and `feat_req` are external; `comp_req` and `aou_req` are authored here. `aou_req` has no `derived_from` because it is an assumption, not a derived requirement.

| Type | `:id:` pattern | Origin | Traceability |
|------|----------------|--------|--------------|
| Stakeholder (`stkh_req`) | `stkh_req__baselibs__...` | External (`score_platform` needs JSON) | Root |
| Feature (`feat_req`) | `feat_req__baselibs__<name>` | External (`score_platform` needs JSON) | `derived_from` >=1 `stkh_req` |
| Component (`comp_req`) | `comp_req__<component>__<name>` | **Authored here** | `derived_from` >=1 `feat_req`; `satisfied_by` the `comp__...` |
| Assumption of Use (`aou_req`) | `aou_req__<component>__<name>` | **Authored here** | No `derived_from`; linked to by a requirement via `covers` |

Other IDs: requirements document `doc__<component>_requirements`; architecture component `comp__baselibs_<component>`.

- The directive title is a short noun phrase; put `shall`, `must`, `will`, and other normative wording in the body.
- Use `:status: invalid` with a TODO instead of linking to a `feat_req` that does not exist yet.
- Do not rename an existing `:id:` casually; downstream links and `:need:` references depend on it.

## Requirement Attributes

### Authored need attributes

| Option | Required | Values / Notes |
|--------|----------|----------------|
| directive title | Yes | Short noun phrase, no `shall` / `must` / `will` |
| `:id:` | Yes | Stable ID matching the need type pattern |
| `:reqtype:` | Yes | `Functional` \| `Interface` \| `Process` \| `Non-Functional` |
| `:safety:` | Yes | `QM` \| `ASIL_B` |
| `:security:` | Yes | `YES` \| `NO` |
| `:status:` | Yes | `valid` \| `invalid` |
| `:version:` | Yes | Whole number, starting at `1`; bump on material change |
| body (content) | Yes | Normative text for `comp_req`; integrator/user assumption for `aou_req` |

### `comp_req`-only attributes

| Option | Required | Values / Notes |
|--------|----------|----------------|
| `:derived_from:` | Yes | Comma-separated `feat_req__...` IDs, **each version-pinned** as `feat_req__...[version==N]` |
| `:satisfied_by:` | Recommended | Implementing `comp__...[version==N]` |
| `:covers:` | Optional | `aou_req__...[version==N]` this requirement covers |
| `:reqcovered:` / `:testcovered:` | Optional | `YES` \| `NO` |
| `:tags:` | Optional | Comma-separated tags |

`aou_req` has no type-specific required attributes and no `:derived_from:` link.

## Safety Classification (ASIL)

`safety` is either `QM` or `ASIL_B`. A `QM` requirement may not be `derived_from` an `ASIL_B` requirement; re-check the chain whenever you change safety.

## Need Versioning

Every need has a whole-number `:version:`. Bump it on material body, safety, security, or link changes; typo and formatting fixes do not need a bump.

Every link value is written as `<target_id>[version==N]`, where `N` is the current version of the target need:

```rst
:derived_from: feat_req__baselibs__containers_library[version==2]
:satisfied_by: comp__baselibs_containers[version==1]
```

- Pin every target in every link attribute, including `:derived_from:`, `:satisfied_by:`, `:realizes:`, `:covers:`, `:fulfils:`, `:includes:`, `:included_by:`, `:complies:`, and `:mitigated_by:`.
- For comma-separated links, pin each ID individually.
- On unresolved-link failures, check the target version as well as the target ID.

## Directory Structure

Author `comp_req` / `aou_req` in each component's `requirements/index.rst`:

```
docs/baselibs/components/<component>/docs/
├── index.rst # component document node (doc__<component>)
├── requirements/index.rst # comp_req + aou_req <- edit here
└── architecture/index.rst # component architecture (comp__..., interfaces)
```

Each file opens with a `.. document::` need, then groups `comp_req` and `aou_req` directives under local headings such as Functional Requirements, Non-Functional Requirements, and Assumptions of Use.

## Building & Validating

```bash
bazel run //:docs
```

A build failure reporting a missing/invalid option, an unknown link target, or a broken reference means a requirement violates the metamodel and must be fixed.

If validation behaves unexpectedly, inspect the authoritative metamodel in the Bazel output base: run `bazel info output_base`, then read `*score_metamodel*/metamodel.yaml` under that directory.
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# Test-to-Requirement Linking

Use this reference when linking, migrating, or reviewing C++ gtest and Rust unit tests against baselibs `comp_req__...` needs. Python tests are out of scope for this skill.

## Routes

### Define or update a test link

1. Identify the `comp_req__<component>__...` that the test verifies; author the requirement first if it does not exist.
2. Decide whether the test alone verifies the requirement (`FullyVerifies`) or contributes partial coverage (`PartiallyVerifies`).
3. Choose `TestType` and `DerivationTechnique` from [Metadata Reference](#metadata-reference).
4. Add a concise `Description` that states objective, input, and expected outcome.
5. Record the metadata using the language-specific mechanism below.
6. Run the narrow test target, usually `bazel test --config=bl-x86_64-linux //score/<component>/...`; the route is complete when the test passes and the referenced requirement exists.

### Migrate legacy metadata

1. Determine what the test actually asserts and find the matching `comp_req__...`.
2. Replace legacy linkage with `FullyVerifies` or `PartiallyVerifies`.
3. Add missing `TestType`, `DerivationTechnique`, and `Description`.
4. Remove non-mandated metadata such as `ASIL` and `Priority`.
5. Run the affected tests; the route is complete when no legacy metadata remains in the touched tests.

| Legacy pattern | Compliant replacement |
|----------------|-----------------------|
| `RecordProperty("Verifies", "...")` | `FullyVerifies`, or `PartiallyVerifies` if several tests together cover the need |
| `SCR-*` ID | The corresponding docs-as-code `comp_req__...` ID |
| Raw C++ symbol, e.g. `::score::json::ToJsonAny` | The `comp_req__...` need implemented by that symbol |
| `ASIL`, `Priority` | Remove; safety belongs on the requirement, not the test |

### Review test linkage

Check every touched test case:

- It records `TestType`, `DerivationTechnique`, `Description`, and exactly one linkage style: `FullyVerifies` or `PartiallyVerifies`.
- Linked unit tests point at existing `comp_req__...` needs, not feature or stakeholder requirements.
- `FullyVerifies` is used only when this test alone covers the requirement; otherwise use `PartiallyVerifies`.
- The assertions exercise the linked requirement's normative behavior.
- No legacy `Verifies`, `ASIL`, `Priority`, `SCR-*`, or raw-symbol requirement links remain.
- The PR approver is not an author of the change.

## Metadata Reference

Every test case that links to requirements shall carry these metadata properties. Multiple IDs are written as one comma-separated string, e.g. `"comp_req__json__number_parsing, comp_req__json__whitespace"`.

| Metadata key | Required | Meaning |
|--------------|----------|---------|
| `FullyVerifies` | One of `FullyVerifies` / `PartiallyVerifies` is mandatory | Requirement/design/interface ID(s) fully covered by this single test |
| `PartiallyVerifies` | See above | Requirement/design/interface ID(s) partially covered by this test |
| `Description` | Yes, non-empty | Objective, inputs, expected outcome; add environment or event sequence only when relevant |
| `TestType` | Yes | `requirements-based`, `interface-test`, `fault-injection`, or `resource-usage` |
| `DerivationTechnique` | Yes | One of the values below |

`DerivationTechnique` is chosen from how the test case was derived:

| Use | When the test is derived from... |
|-----|--------------------------------|
| `requirements-analysis` | The requirement's normative statement |
| `design-analysis` | Design or architecture rather than requirement text |
| `boundary-values` | Limits, overflow, out-of-bounds, zero, min/max, saturation, off-by-one |
| `equivalence-classes` | One representative of a class with equivalent behavior |
| `fuzz-testing` | Randomized or generated inputs |
| `error-guessing` | Reasoning about likely faults without a formal derivation |
| `explorative-testing` | Ad-hoc manual exploration |

When several techniques apply, choose the one that best explains why this specific case was written. A max-value or overflow case is `boundary-values` even if the feature came from requirements analysis.

## Description

Keep `Description` to one or two sentences. It must be self-contained enough to state objective, input, and expected outcome, but it should not restate every visible value from the test body.

Good:

```text
"Check that tuple_to_array converts a tuple into a std::array of matching size and element order."
"Check that strip_trailing_spaces clamps end=16 on an 11-character string and returns 16."
```

Avoid descriptions that only repeat the test name, or mechanical `Objective` / `Input` / `Expected` prose that mirrors the code without adding information.

## C++ gtest

Use `RecordProperty`. Put common fixture-wide metadata in `SetUp()` only when the whole fixture shares the same values; keep per-test linkage and `Description` inside each test case.

```cpp
class MyComponentTest : public ::testing::Test
{
protected:
void SetUp() override
{
RecordProperty("TestType", "requirements-based");
RecordProperty("DerivationTechnique", "requirements-analysis");
}
};

TEST_F(MyComponentTest, GivenValidInput_WhenParsed_ThenReturnsExpectedValue)
{
RecordProperty("PartiallyVerifies", "comp_req__json__number_parsing");
RecordProperty("Description", "Check that parsing a valid integer literal yields its numeric value.");

EXPECT_EQ(/* ... */);
}
```

For a free `TEST(...)`, record every property inside the test body:

```cpp
TEST(MyComponentTest, GivenFullQueue_WhenPush_ThenReturnsFalse)
{
RecordProperty("TestType", "interface-test");
RecordProperty("DerivationTechnique", "boundary-values");
RecordProperty("PartiallyVerifies", "comp_req__concurrency__bounded_queue");
RecordProperty("Description", "Check that pushing into a full queue returns false instead of blocking.");
EXPECT_FALSE(/* ... */);
}
```

Do not create a fixture just to deduplicate mixed metadata. Repeating `TestType` and `DerivationTechnique` across free tests is acceptable.

## Rust

Use `#[record_property("Key", "Value")]` from the `test_properties` crate on each `#[test]` function.

```rust
use test_properties::record_property;

#[record_property("PartiallyVerifies", "comp_req__containers__dynamic_array")]
#[record_property("Description", "Check that pushing beyond capacity returns an error instead of growing.")]
#[record_property("TestType", "requirements-based")]
#[record_property("DerivationTechnique", "boundary-values")]
#[test]
fn given_full_vector_when_push_then_returns_error() {
assert!(/* ... */);
}
```

The `test_properties` crate must be an approved dependency. If it is not available, ask before adding or inventing a substitute. In-crate `#[cfg(test)]` unit tests map to unit testing; tests under a crate's `tests/` directory map to component/feature integration testing.
Loading