pytest-given Self-Report
pytest 9.0.3 · pytest-given 0.1.0 ·
Module:
A scenario records under its node ID
✓ passed 0ms
Given
a fresh Collector
When
a Scenario starts under its Node ID and finishes
Then
it carries its Node ID, name, status and Tag
A scenario is timed from past its step fixture setup
✓ passed 0ms
Given
a Collector whose clock reads 100.3s once setup is done
When
the clock is started past setup and the body runs 0.2s
Then
the recorded duration is the body alone, not the setup before it
Steps record with their phases
✓ passed 0ms
Given
an Active scenario in a fresh Collector
When
a given and a when Step are pushed
Then
each Step carries its Phase
Steps pushed during fixture setup record into the fixture recording
✓ passed 0ms
Given
a Fixture recording under setup
When
a Step is pushed inside the fixture body
Then
it is recorded as a child of the recording root
An attachment lands on the step being recorded
✓ passed 0ms
Given
a Fixture recording under setup
When
an Attachment is attached inside the fixture body
Then
the Attachment lands on the recording root
Fixture-body steps do not leak into the active scenario
✓ passed 0ms
Given
an Active scenario with a Fixture recording
When
a Step is pushed inside the fixture body
Then
the step lives only in the recording, not the scenario
An attachment outside every step is refused
✓ passed 0ms
Given
an Active scenario with no Step open
When
an attachment is made from the test body
Then
it is refused rather than dropped
A fixture recording is deep-copied when grafted
✓ passed 0ms
Given
a Fixture recording with a nested child Step
When
a Graft copies it into the Active scenario
Then
the scenario gains a deep copy of the recorded steps
A step fixture failing in teardown fails its finished scenario
✓ passed 0ms
Given
a Scenario that already finished as passed
When
a fixture raises past its yield, after the scenario finished
Then
the recorded scenario carries the failure
A teardown failure keeps the error the scenario already carries
✓ passed 0ms
Given
a Scenario that already failed in its body
When
its fixture then also fails in teardown
Then
the body failure is what the report shows
A teardown failure under an unknown Node ID is ignored
✓ passed 0ms
Given
a Collector that recorded one scenario
When
a teardown fails under a node id no scenario claimed
Then
the recorded scenario is untouched
A leaf given is grafted as a childless given step
✓ passed 0ms
Given
an Active scenario is being recorded
When
a leaf Graft appends a childless Step
Then
the step is a given with no children
Grafting with an override replaces the root label but keeps children
✓ passed 0ms
Given
a Fixture recording whose root has a label and a child
When
a Graft supplies an override Narration
Then
the grafted root shows the override text and keeps its children
Grafting with no active scenario is a no-op
✓ passed 0ms
Given
a collector with no Active scenario
When
a leaf Graft runs
Then
no scenario is recorded
FileGlossary lookup is case-insensitive
✓ passed 0ms
Given
a File glossary loaded from a Markdown file
Glossary file
# Glossary

| Term | Meaning |
|------|---------|
| Guest  | A person booking. |
| Room   | A bookable room. |
| search | Look up options. |
When
the same Term is looked up in three different cases
Then
every lookup resolves to one handle type and the same id
Repeated lookups return the same handle
✓ passed 0ms
Given
a File glossary loaded from a Markdown file
Glossary file
# Glossary

| Term | Meaning |
|------|---------|
| Guest  | A person booking. |
| Room   | A bookable room. |
| search | Look up options. |
When
the same Term is looked up twice
Then
both lookups return the one memoized handle
File-loaded terms start kindless
✓ passed 0ms
Given
a Markdown glossary file with no kind column
Glossary file
# Glossary

| Term | Meaning |
|------|---------|
| Guest  | A person booking. |
| Room   | A bookable room. |
| search | Look up options. |
When
a File glossary loads it
Then
each Term is Kindless until Kind inference runs
An unknown name raises with a suggestion
diagnostics validation ✓ passed 0ms
Given
a File glossary loaded from a Markdown file
Glossary file
# Glossary

| Term | Meaning |
|------|---------|
| Guest  | A person booking. |
| Room   | A bookable room. |
| search | Look up options. |
When
a misspelt Term is looked up
Then
a PytestGivenError is raised with a spelling hint
Handles are usable inline in an activity
✓ passed 0ms
Given
a File glossary loaded from a Markdown file
Glossary file
# Glossary

| Term | Meaning |
|------|---------|
| Guest  | A person booking. |
| Room   | A bookable room. |
| search | Look up options. |
When
its handles build an Activity
Then
each slot becomes a Term ref
Calling a handle overrides its display
✓ passed 0ms
Given
a File glossary loaded from a Markdown file
Glossary file
# Glossary

| Term | Meaning |
|------|---------|
| Guest  | A person booking. |
| Room   | A bookable room. |
| search | Look up options. |
When
a handle is called to name an Instance
Then
the Term ref carries the overridden display
An explicit kind column sets term kinds
✓ passed 0ms
Given
a Markdown glossary with an explicit Kind column
Glossary file
| Term | Meaning | Kind |
|---|---|---|
| Guest | x | Actor |
| Room | y | Work Object |
When
the File glossary reads the Kind column
Then
kinds come straight from the file, not Kindless inference
A kind column can be selected by integer index
✓ passed 0ms
Given
a Markdown glossary with the kind in the third column
Glossary file
| Term | Meaning | Kind |
|---|---|---|
| Guest | x | Actor |
| Room | y | Work Object |
When
the File glossary selects the kind column by index
Then
the kinds are read from that column
A work_object kind alias maps to the object kind
✓ passed 0ms
Given
a glossary whose Kind cell says work_object
Glossary file
| Term | Meaning | Kind |
|---|---|---|
| Room | y | work_object |
When
the File glossary parses the kind
Then
it normalizes to the Work Object kind
An unrecognized kind value is rejected
diagnostics validation ✓ passed 0ms
Given
a glossary whose Kind cell holds an unknown value
Glossary file
| Term | Meaning | Kind |
|---|---|---|
| Guest | x | Wizard |
When
the File glossary loads the file
Then
a PytestGivenError names the unrecognized kind
A missing glossary file is reported clearly
validation ✓ passed 0ms
Given
a path to a file that does not exist
When
a File glossary is opened on that path
Then
a PytestGivenError reports the file is not found
A term cell with no alphanumeric characters is rejected
diagnostics validation ✓ passed 0ms
Given
a row whose Term cell has no id-able characters
Glossary file
| Term | Meaning |
|---|---|
| @#$ | some definition |
When
the File glossary loads the file
Then
a PytestGivenError is raised with file:line context
Conflicting duplicate rows are rejected
validation ✓ passed 0ms
Given
two rows for one Term with different definitions
Glossary file
| Term | Meaning |
|---|---|
| Guest | First definition. |
| Guest | Second definition. |
When
the File glossary loads the file
Then
a PytestGivenError reports the conflicting rows
A blank description normalizes to undefined
✓ passed 0ms
Given
a row whose description cell is blank
Glossary file
| Term | Meaning |
|---|---|
| Guest |   |
When
the File glossary parses it
Then
the Term definition is None, i.e. Undefined
Identical duplicate rows collapse to one term
✓ passed 0ms
Given
two identical rows for the same Term
Glossary file
| Term | Meaning |
|---|---|
| Guest | A person booking. |
| Guest | A person booking. |
When
the File glossary parses them
Then
they collapse to a single Term
Calling FileGlossary looks up a known term
✓ passed 0ms
Given
a File glossary loaded from a Markdown file
Glossary file
# Glossary

| Term | Meaning |
|------|---------|
| Guest  | A person booking. |
| Room   | A bookable room. |
| search | Look up options. |
When
a known Term is looked up by call
Then
a Deferred term is returned
FileGlossary is a closed vocabulary
validation ✓ passed 0ms
Given
a File glossary loaded from a Markdown file
Glossary file
# Glossary

| Term | Meaning |
|------|---------|
| Guest  | A person booking. |
| Room   | A bookable room. |
| search | Look up options. |
When
an unknown name is called
Then
a PytestGivenError is raised
no new Term was created
Calling an actor names a distinct instance
✓ passed 0ms
Given
an Actor handle for Guest
When
the Actor is called with a name
Then
an Instance with a distinct display is returned
Calling a verb records an inflection of the same term
✓ passed 0ms
Given
a Verb handle for confirm
When
the Verb is called with a surface form
Then
an Inflection sharing the verb identity is returned
Registering an actor returns a typed handle
✓ passed 0ms
Given
an empty glossary
When
an Actor is registered with a definition
Then
a typed Actor handle with the Actor kind is returned
Re-registering a term with matching fields is idempotent
✓ passed 0ms
Given
an Actor already registered with a definition
When
the same name and definition are registered again
Then
both handles share the one Term
Re-registering a term with a different definition is rejected
validation ✓ passed 0ms
Given
an Actor already registered with one definition
When
the name is registered again with a different definition
Then
a PytestGivenError reports the conflict with the prior registration
The same name cannot be two different kinds
validation ✓ passed 0ms
Given
a name already registered as an Actor
When
the same name is registered as a Verb
Then
a PytestGivenError reports the conflict with the prior registration
Registering an actor captures its definition site
✓ passed 0ms
Given
a rootdir-aware glossary
When
an Actor is registered
Then
the Term records a Source link to this file
Calling the glossary declares a kindless term
✓ passed 0ms
Given
an empty glossary
When
a Term is declared by call, without a kind
Then
the Term is registered as Kindless
Subscript looks up an already-declared term
✓ passed 0ms
Given
a glossary with one declared Term
When
the name is looked up by subscript
Then
the returned Term is the declared one
Subscripting an unknown name raises with a hint
diagnostics validation ✓ passed 0ms
Given
a glossary with one declared Term
When
a near-miss name is subscripted
Then
a PytestGivenError is raised with a spelling hint
Term kinds are inferred from activity-slot positions
✓ passed 0ms
Given
a glossary of three Kindless Term entries
When
Kind inference runs over a Story
Then
they are inferred as Actor, Verb, Work Object by slot
An actor slot anywhere wins over a noun slot elsewhere
✓ passed 0ms
Given
a Term that sits in a noun slot in one Story
When
the same Term also appears in an Actor slot
Then
its inferred kind is Actor
A term used in no story stays kindless
✓ passed 0ms
Given
a Term referenced by no Story
When
Kind inference runs with no stories
Then
the Term remains Kindless
A term in both a verb and a noun slot is a conflict
diagnostics validation ✓ passed 0ms
Given
a Kindless Term used in a verb slot and a noun slot
When
kind resolution runs over both stories
Then
a PytestGivenError names the conflicting term
A declared kind consistent with its slot is kept
✓ passed 0ms
Given
a glossary with explicitly declared Term kinds
When
Kind inference runs over a matching Story
Then
the declared kinds are verified and preserved
A declared verb in an actor slot is rejected
diagnostics validation ✓ passed 0ms
Given
a Term declared as a Verb
When
kind resolution places it in the Actor slot
Then
a PytestGivenError names the misplaced term
A term used as both verb and actor is a conflict
diagnostics validation ✓ passed 0ms
Given
a Kindless Term used in a verb slot and an actor slot
When
kind resolution runs over both stories
Then
a PytestGivenError names the conflicting term
A declared work object in an actor slot is rejected
diagnostics validation ✓ passed 0ms
Given
a Term declared as a Work Object
When
kind resolution places it in the Actor slot
Then
a PytestGivenError names the misplaced term
A declared actor in a verb slot is rejected
validation ✓ passed 0ms
Given
a Term declared as an Actor
When
kind resolution places it at position 1 (the verb slot)
Then
a PytestGivenError says an actor cannot fill the verb slot
A conflict error names only the offending stories
diagnostics validation ✓ passed 0ms
Given
an Actor Term that also appears in a verb slot
When
kind resolution raises
Then
only the offending story is named in the message
A conflict message excludes stories with an unrelated slot
diagnostics validation ✓ passed 0ms
Given
a Kindless Term used in verb, actor and noun slots
When
the verb-vs-actor conflict is raised
Then
only the verb and actor stories are named, not the noun one
A declared verb in a noun slot is rejected
validation ✓ passed 0ms
Given
a Term declared as a Verb
When
kind resolution places it at position ≥2 (a noun slot)
Then
a PytestGivenError says a verb cannot fill the noun slot
Slot positions alternate verb/noun after the actor
✓ passed 0ms
Given
the five positions of a short activity path
When
the Slot rule is applied to each position
Then
position 0 is the actor Slot, then verb and noun alternate
A pipe table parses into term and definition rows
markdown ✓ passed 0ms
Given
a Markdown document with one pipe table
Markdown document
# Glossary

| Term | Meaning |
|------|---------|
| Guest | A person booking. |
| Room  | A bookable room. |
When
the parser reads it into rows for a File glossary
Then
each row carries a Term, definition and source line
Multiple tables in one file are merged
markdown ✓ passed 0ms
Given
a document containing two separate pipe tables
Markdown document
# Glossary

| Term | Meaning |
|------|---------|
| Guest | A person booking. |
| Room  | A bookable room. |

## More

| Term | Meaning |
|---|---|
| Search | Look up. |
When
the parser reads the whole document
Then
every table contributes its Term rows
Columns can be selected by header name
markdown ✓ passed 0ms
Given
a table with custom, differently-cased header names
Markdown document
| Word | Note | Role |
|---|---|---|
| Guest | x | Actor |
When
the parser selects columns by header name
Then
the named columns are matched case-insensitively
Escaped pipes are preserved in cells
markdown ✓ passed 0ms
Given
cells containing escaped pipe characters (\|)
Markdown document
| Term | Meaning |
|---|---|
| A\|B | pipe\|here |
When
the parser splits the row
Then
the escaped pipe survives as a literal pipe
Tables inside fenced code blocks are skipped
markdown ✓ passed 0ms
Given
a fenced code block that contains a look-alike table
Markdown document
```
| Term | Meaning |
|---|---|
| Fake | nope |
```

| Term | Meaning |
|---|---|
| Real | yes |
When
the parser reads the document
Then
only the real table outside the fence contributes rows
A file with no pipe table is rejected
markdown validation ✓ passed 0ms
Given
a document with no pipe table
Markdown document
# Just a heading

No tables here.
When
the parser reads it for a File glossary
Then
a PytestGivenError reports that the file has no pipe table
A missing named column is rejected
diagnostics markdown validation ✓ passed 0ms
Given
a Markdown document with one pipe table
Markdown document
# Glossary

| Term | Meaning |
|------|---------|
| Guest | A person booking. |
| Room  | A bookable room. |
When
the parser selects a header name that is absent
Then
a PytestGivenError names the missing column
A column index out of range is rejected
diagnostics markdown validation ✓ passed 0ms
Given
a Markdown document with one pipe table
Markdown document
# Glossary

| Term | Meaning |
|------|---------|
| Guest | A person booking. |
| Room  | A bookable room. |
When
the parser selects a column index past the table width
Then
a PytestGivenError names the out-of-range column
A data row with too few columns is rejected
diagnostics markdown validation ✓ passed 0ms
Given
a table with a data row narrower than its header
Markdown document
| Term | Meaning | Type |
|---|---|---|
| Guest | A person |
| Room | A bookable room | place |
When
the parser reads the short row
Then
a PytestGivenError points at the short row
Bold term cells render as clean terms
markdown ✓ passed 0ms
Given
a Term cell written with **bold** emphasis
Markdown document
| Term | Meaning |
|---|---|
| **Scenario** | A decorated test. |
When
the parser reads the term cell
Then
the emphasis is unwrapped to the plain canonical
Italic and inline-code term cells are unwrapped
markdown ✓ passed 0ms
Given
Term cells using *italic* and `code` emphasis
Markdown document
| Term | Meaning |
|---|---|
| *Step* | one. |
| `given` | two. |
When
the parser reads the term cells
Then
each unwraps to its plain text
Underscores inside an identifier survive
markdown ✓ passed 0ms
Given
a Term literally named work_object
Markdown document
| Term | Meaning |
|---|---|
| work_object | a thing. |
When
the parser reads the term cell
Then
the single underscores are not treated as emphasis
Emphasis is stripped from kind cells too
markdown ✓ passed 0ms
Given
a Kind cell written with bold emphasis
Markdown document
| Term | Meaning | Kind |
|---|---|---|
| Guest | x | **Actor** |
When
the parser reads the kind cell
Then
the kind is unwrapped to plain text
Definition markdown is left intact
markdown ✓ passed 0ms
Given
a definition cell rich with inline code
Markdown document
| Term | Meaning |
|---|---|
| Scenario | A test decorated with `@scenario(...)`. |
When
the parser reads the row
Then
the definition keeps its markup for the tooltip
A pipe line without a separator is not a table
markdown ✓ passed 0ms
Given
prose containing a stray pipe, then a real table
Markdown document
This line has a | in it but no separator follows.
Next line is not a separator.

| Term | Meaning |
|---|---|
| Real | yes |
When
the parser reads the document
Then
only the real pipe table produces rows
A step pairs its narration with a phase
✓ passed 0ms
When
a given Step descriptor is created
Then
it carries the given Phase and its Narration
when_then records the action and its outcome as siblings
✓ passed 0ms
Given
an Active scenario in a local Collector
When
a when_then block exits cleanly
Then
a when and a sibling then Step are recorded
when_then pairs with an inner pytest.raises
✓ passed 0ms
Given
an Active scenario in a local Collector
When
the when_then body raises and an inner pytest.raises swallows it
Then
both sibling steps are still recorded
when_then omits the then when the body raises uncaught
validation ✓ passed 0ms
Given
an Active scenario in a local Collector
When
the when_then body raises with nothing catching inside
Then
only the when step is recorded — the outcome never held
A nested when becomes a child of the when_then action
✓ passed 0ms
Given
an Active scenario in a local Collector
When
a when opens inside the when_then body
Then
the sub-action is a child of the action and the then still follows
An actor handle in a path becomes a term ref
✓ passed 0ms
Given
a Guest actor
a search verb
a Room work object
When
a Path is built from three glossary handles
Then
the Actor slot becomes a Term ref
An inflected verb keeps its term identity but shows the inflection
✓ passed 0ms
Given
a Guest actor
a search verb
a Room work object
a Verb handle called with an Inflection
When
it takes the verb slot of a Path
Then
the Term ref shows the inflection over the same Verb
A bare string in a path becomes a connective word
✓ passed 0ms
Given
a Guest actor
a search verb
a Room work object
When
a Path is built with a bare word between term nodes
Then
the bare word becomes an Activity Part word, not a Term ref
A path needs at least an actor, a verb and a node
validation ✓ passed 0ms
Given
a Guest actor
a search verb
When
a Path of only two parts is built
Then
a PytestGivenError rejects it as too short
Position 0 of a path must be an actor
validation ✓ passed 0ms
Given
a search verb
a Room work object
When
a Path is built with a Work Object in position 0
Then
a PytestGivenError says position 0 is the Actor slot
A verb cannot open a path
validation ✓ passed 0ms
Given
a Guest actor
a search verb
a Room work object
When
a Verb is placed in position 0 of a Path
Then
a PytestGivenError says position 0 is the Actor slot
A bare string may stand in for the actor slot
✓ passed 0ms
Given
a search verb
a Room work object
When
a bare string takes position 0 of a Path
Then
it is accepted as an Activity Part word
Position 1 of a path must be a verb
validation ✓ passed 0ms
Given
a Guest actor
a Room work object
When
an Actor is placed in position 1 of a Path
Then
a PytestGivenError says position 1 is the Verb slot
A work object cannot fill the verb slot
validation ✓ passed 0ms
Given
a Guest actor
a Room work object
When
a Work Object is placed in position 1 of a Path
Then
a PytestGivenError says position 1 is the Verb slot
Position 2 of a path must be a noun
validation ✓ passed 0ms
Given
a Guest actor
a search verb
When
a Verb is placed in position 2 of a Path
Then
a PytestGivenError says position 2 is the noun slot
A bare verb may sit between two real entity nodes
✓ passed 0ms
Given
a Guest actor
a Room work object
When
a bare verb sits between an Actor and a Work Object
Then
the entities are term refs and the verb stays a bare word
A path may be fully bare words
✓ passed 0ms
Given
three plain words with no glossary handles
When
a Path is built from them
Then
every part is an Activity Part word
Node/edge alternation allows a trailing connective node
✓ passed 0ms
Given
an Actor, a Verb, a Work Object and a second actor
When
they form a five-part Path joined by a connective
Then
even positions are term-ref nodes and the connective stays a word
A path may not end on a dangling edge
validation ✓ passed 0ms
Given
an Actor, Verb and Work Object plus a connective
When
a path ending on a connective edge is built
Then
a PytestGivenError rejects the dangling edge
A single-path activity synthesizes one path
✓ passed 0ms
Given
a Guest actor
a search verb
a Room work object
When
an Activity is built from handles directly
Then
it wraps a single Path
An activity may branch into multiple paths
✓ passed 0ms
Given
a Guest actor
a search verb
a Room work object
two alternate Path branches
When
they are combined into one Activity
Then
the activity carries both paths
Mixing loose parts and prebuilt paths is rejected
validation ✓ passed 0ms
Given
a Guest actor
a search verb
a Room work object
a prebuilt Path
When
it is combined with loose handles in one Activity
Then
a PytestGivenError rejects the mix
Activity id 0 is reserved
validation ✓ passed 0ms
Given
a Guest actor
a search verb
a Room work object
When
an Activity is built with explicit activity_id=0
Then
a PytestGivenError says activity_id=0 is reserved
A story auto-numbers its activities from one
✓ passed 0ms
Given
a Guest actor
a search verb
a Room work object
When
a Story is built from two Activity rows
Then
the activities are numbered 1 and 2
Auto-numbering skips ids already taken explicitly
✓ passed 0ms
Given
a Guest actor
a search verb
a Room work object
a mix of explicit and auto Activity ids
When
they are assembled into a Story
Then
auto picks skip the ids already used explicitly
Duplicate activity ids in a story are rejected
validation ✓ passed 0ms
Given
a Guest actor
a search verb
a Room work object
two Activity rows sharing an explicit id
When
they are assembled into a Story
Then
a PytestGivenError reports the duplicate activity id
A story derives its id from its title
✓ passed 0ms
Given
a human-readable story title
When
a Story is built from it
Then
its id is the slugified title
A story may span only one glossary
validation ✓ passed 0ms
Given
a Guest actor
a search verb
a Room work object
two activities that reach two different glossaries
When
a Story is built spanning both glossaries
Then
a PytestGivenError says a story spans multiple glossaries
Two stories with the same id collide
validation ✓ passed 0ms
Given
a Story already declared under an id
When
a second story is declared with the same slug
Then
a PytestGivenError reports the id was already declared
A path may chain a second verb-object pair
✓ passed 0ms
Given
an Actor, two Verb and two Work Object handles
When
they form a five-node Path (actor verb object verb object)
Then
every slot is a Term ref, with no bare words
A declared work object in a verb slot is rejected at construction
validation ✓ passed 0ms
Given
a File glossary declaring Room a work object
When
Room is placed in the verb slot
Then
a PytestGivenError names the term and its declared kind
A slot error names the term, not its repr
diagnostics ✓ passed 0ms
Given
a Guest actor
a Room work object
a search verb
When
a work object is placed in the verb slot
Then
the message names the term without dumping the glossary
the message is short and free of dataclass reprs
A kindless term stays valid in any slot
validation ✓ passed 0ms
Given
a Kindless Term declared with g(...)
When
it is placed in a node slot and a verb slot
Then
both paths construct, leaving the kind to inference
A non-handle activity part names its type
validation diagnostics ✓ passed 0ms
Given
a Guest actor
a Room work object
When
an int is passed where a verb handle belongs
Then
a PytestGivenError names the offending type and the path
A Template parses a bare placeholder
parametrization ✓ passed 0ms
Given
a deferred Templatize template with one placeholder
When
the template is parsed
Then
it splits into literal and placeholder Narration parts
A Template substitutes parametrize values
parametrization ✓ passed 0ms
Given
a Templatize template referencing a Case column
When
a Parameter table value is substituted in
Then
the placeholder is filled with that value
A t-string interpolation becomes a value part
✓ passed 0ms
Given
a t-string step with one interpolated value
When
the t-string is parsed at runtime
Then
the interpolation becomes a Narration value part
A t-string can interpolate an arbitrary expression
✓ passed 0ms
Given
a t-string step interpolating a computed expression
When
the t-string is parsed
Then
the Value highlight part records the full expression
A glossary handle in a t-string emits a term ref
✓ passed 0ms
Given
an Actor handle from the glossary
When
the handle is interpolated into a t-string step
Then
the step carries a Term ref for that Actor
A work object handle in a t-string emits a term ref
✓ passed 0ms
Given
a Work Object handle from the glossary
When
it is interpolated into a t-string step
Then
the step carries a Term ref for that Work Object
A bare verb handle keeps its canonical display
✓ passed 0ms
Given
a Verb handle used without an Inflection
When
it is interpolated into a t-string step
Then
the Term ref shows the canonical verb
An inflected verb in a t-string shows the inflection
✓ passed 0ms
Given
a Verb handle called with an Inflection
When
it is interpolated into a t-string step
Then
the Term ref shows the inflection but keeps the verb id
A term ref may not carry a format spec
validation ✓ passed 0ms
Given
an Actor handle interpolated with a format spec
When
the t-string is parsed
Then
a PytestGivenError says a Term ref takes no format spec
A FileGlossary handle works in a t-string step
✓ passed 0ms
Given
a Deferred term from a File glossary
When
it is interpolated into a t-string step
Then
the step carries a single Term ref
The glossary view aggregates instances and verb forms
✓ passed 0ms
Given
a Report whose Story and Scenario reference entity Instances and an Inflection
Report data
{
  "metadata": {
    "project": "p",
    "timestamp": "t",
    "pytest_version": "8",
    "plugin_version": "0",
    "commit_sha": null,
    "title": null
  },
  "scenarios": [
    {
      "id": "t",
      "narration": {
        "text": "s",
        "parts": []
      },
      "module": "m",
      "tags": [],
      "status": "passed",
      "duration_ms": 0,
      "steps": [
        {
          "phase": "when",
          "narration": {
            "text": "x",
            "parts": [
              {
                "term_id": "guest",
                "display": "Alice",
                "expression": ""
              },
              {
                "term_id": "search",
                "display": "searches",
                "expression": ""
              },
              {
                "term_id": "room",
                "display": "Deluxe Suite",
                "expression": ""
              }
            ]
          },
          "children": [],
          "attachments": [],
          "activity_ids": [],
          "fixture_name": null
        }
      ],
      "parameters": null,
      "error": null,
      "skip_reason": null,
      "source": null,
      "story_id": "book",
      "activity_ids": []
    }
  ],
  "glossary": {
    "terms": [
      {
        "id": "guest",
        "kind": "actor",
        "canonical": "Guest",
        "definition": null,
        "source": null
      },
      {
        "id": "room",
        "kind": "object",
        "canonical": "Room",
        "definition": null,
        "source": null
      },
      {
        "id": "search",
        "kind": "verb",
        "canonical": "search",
        "definition": null,
        "source": null
      }
    ]
  },
  "stories": [
    {
      "id": "book",
      "title": "Book",
      "activities": [
        {
          "id": 1,
          "paths": [
            {
              "parts": [
                {
                  "term_id": "guest",
                  "display": "Alice"
                },
                {
                  "term_id": "search",
                  "display": "searches for"
                },
                {
                  "term_id": "room",
                  "display": "Deluxe Suite"
                }
              ]
            }
          ]
        }
      ],
      "source": null
    }
  ]
}
When
the Glossary aggregations are built
Then
the entity terms collect their Instances
the verb collects its Inflection but not its canonical form
Terms referenced by an activity record the story
✓ passed 0ms
Given
a Story whose Activity references an actor and a verb
When
the Glossary aggregations are built
Then
the actor and the verb each list that Story
A story referencing a term twice lists it once
✓ passed 0ms
Given
a Story whose two activities repeat the same Term and the same Inflection
When
the Glossary aggregations are built
Then
the Story and the Inflection appear once each
A canonical entity reference is not an instance
✓ passed 0ms
Given
a Story activity and a Step referencing entities by canonical name only
When
the Glossary aggregations are built
Then
neither entity term records an Instance
A kindless term records only its story ref
✓ passed 0ms
Given
a Kindless Term referenced by a Story activity
When
the Glossary aggregations are built
Then
the Term lists the Story but no Instance and no Inflection
An instance seen in a fixture step records its fixture provenance
✓ passed 0ms
Given
a Scenario whose fixture-sourced Step names an Instance
When
the Glossary aggregations are built
Then
the Instance carries the fixture name
The term index maps each term to its scenarios once
✓ passed 0ms
Given
a Scenario referencing one Term in two steps and another in its name
When
the term-scenario index is built
Then
each Term maps to the scenario exactly once
An under-anchored activity is flagged ineligible in rollups
✓ passed 0ms
Given
a Story with an anchored and an under-anchored Activity
When
the story rollups are built
Then
only the anchored Activity is Coverage-eligible
An Activity is labeled by the prose of its paths
✓ passed 0ms
Given
a Story with a two-path activity
When
the activity labels are built
Then
the label reads as prose under a story-scoped key, with the path texts joined
A verb activity ref has one identity regardless of inflection
✓ passed 0ms
Given
a Verb written canonically and as an Inflection
When
Coverage derives each Term ref identity
Then
both collapse to the one canonical verb identity
A branching activity unions references across its paths
✓ passed 0ms
Given
an Activity that branches into two Path alternatives
When
Coverage collects the Activity references
Then
both Instance identities across the branches are present
An instance step ref adds a canonical fallback
✓ passed 0ms
Given
a Step referring to a named Instance
When
Coverage computes the identity set for the Step
Then
it includes the canonical Term ref fallback
A verb ref always resolves to its canonical identity
✓ passed 0ms
Given
a Step using an Inflection of a Verb
When
Coverage computes its identity set
Then
the identity ignores the surface form and stays canonical
An unknown term ref is skipped
validation ✓ passed 0ms
Given
a Step referencing a Term not in the glossary
When
Coverage computes its identity set
Then
the unknown ref contributes nothing to the identity set
An instance step covers a canonical activity
✓ passed 0ms
Given
a Story with a canonical Activity
When
a Scenario step names a specific Instance
Then
Coverage reports the Activity as covered
A canonical step does not cover an instance activity
✓ passed 0ms
Given
an Activity anchored to a named Instance
When
a Scenario step only names the canonical Actor
Then
Coverage leaves the more specific instance activity uncovered
Promoting a bare word to a verb ref drops coverage from a step that matched
✓ passed 0ms
Given
a Step naming two term refs
the same Activity with that middle slot a bare word, then a Verb ref
When
Coverage is computed against each Story
Then
the two-ref Activity is covered
the widened Activity is no longer covered
A scenario activity binding constrains coverage
✓ passed 0ms
Given
a Story with two matching activities
When
the Scenario binds only to activity 1
Then
Coverage considers only the bound Activity
An activity with two distinct terms is coverage-eligible
✓ passed 0ms
Given
an Activity anchored by two distinct Term refs
When
its Coverage eligibility is checked
Then
it is eligible for Coverage tracking
An under-anchored activity is not coverage-eligible
✓ passed 0ms
Given
an Activity that mentions only one distinct Term
When
its Coverage eligibility is checked
Then
it is ineligible — Coverage needs at least two anchors
An under-anchored activity is never reported as covered
✓ passed 0ms
Given
a Story whose Activity is all bare words
When
coverage is computed against a scenario
Then
Coverage excludes the under-anchored Activity
Nested steps are walked for coverage
✓ passed 0ms
Given
a Story with one canonical Activity
When
the covering Term refs live in a nested child Step
Then
the nested Step still counts and the Activity is covered
An explicit step binding covers an eligible activity
✓ passed 0ms
Given
a Story with a coverage-eligible Activity
When
a Step binds to it explicitly by id
Then
Coverage counts it directly, without identity matching
An explicit binding still requires eligibility
validation ✓ passed 0ms
Given
a Story whose Activity is under-anchored
When
a Step binds to it explicitly by id
Then
eligibility gates the binding, so Coverage stays empty
Parameter coloring marks placeholders and table headers
✓ passed 58ms
Given
a Report holding a Parametrized scenario with a Parameter table
When
the Renderer renders the HTML page
Then
Parameter coloring classes mark the grouped placeholder and the table headers
the page carries one generated color rule per column, after the stylesheet so a term ref bound to a column takes the column ink
A passed scenario renders as a checked heading with step bullets
✓ passed 0ms
Given
a Report holding a passed Scenario with three steps
Scenario record
{
  "id": "tests/t.py::test_buy",
  "narration": {
    "text": "Buy coffee",
    "parts": []
  },
  "module": "tests/t.py",
  "tags": [
    "billing",
    "happy-path"
  ],
  "status": "passed",
  "duration_ms": 0,
  "steps": [
    {
      "phase": "given",
      "narration": {
        "text": "a machine",
        "parts": []
      },
      "children": [],
      "attachments": [],
      "activity_ids": [],
      "fixture_name": null
    },
    {
      "phase": "when",
      "narration": {
        "text": "I insert $2",
        "parts": []
      },
      "children": [],
      "attachments": [],
      "activity_ids": [],
      "fixture_name": null
    },
    {
      "phase": "then",
      "narration": {
        "text": "I get a coffee",
        "parts": []
      },
      "children": [],
      "attachments": [],
      "activity_ids": [],
      "fixture_name": null
    }
  ],
  "parameters": null,
  "error": null,
  "skip_reason": null,
  "source": null,
  "story_id": null,
  "activity_ids": []
}
When
the Markdown Report is rendered
Then
the heading is checked and each Step is a phase bullet
Nested steps indent under their parent
✓ passed 0ms
Given
a Scenario whose when Step has a nested child
When
the Markdown Report is rendered
Then
the child bullet indents under its parent
Structured narration renders terms, values and placeholders
✓ passed 0ms
Given
a Step whose Narration carries a Term ref, a value and a placeholder
When
the Markdown Report is rendered
Then
the Term ref renders in guillemets, the value verbatim and the placeholder in braces
A parametrized scenario renders its parameter table
parametrization ✓ passed 0ms
Given
a Parametrized scenario with a two-Case Parameter table
When
the Markdown Report is rendered
Then
the heading counts the cases and the Parameter table lists each row
A failing step is marked with a minimal error digest
✓ passed 0ms
Given
a failed Scenario carrying a two-line error and an internal frame
Error record
{
  "message": "ValueError: not sold out\nassert 1 == 0",
  "frames": [
    {
      "path": "/x/_pytest/runner.py",
      "lineno": 1,
      "func": "run",
      "code": "",
      "is_internal": true
    },
    {
      "path": "/x/tests/test_shop.py",
      "lineno": 88,
      "func": "test_sold_out",
      "code": "buy(m)",
      "is_internal": false
    }
  ],
  "error_tail": null
}
When
the Markdown Report is rendered
Then
the heading is crossed and the error follows the steps
only the first message line and the non-internal frame are quoted
A multi-line attachment renders as a fenced block
✓ passed 0ms
Given
a Step carrying a multi-line Attachment
When
the Markdown Report is rendered
Then
the Attachment content sits in an indented fence, not inline
A skipped scenario shows its skip reason
✓ passed 0ms
Given
a skipped Scenario with a reason
When
the Markdown Report is rendered
Then
the heading is marked skipped and the reason follows the node id
Grouping collapses parametrize cases into one scenario
parametrization ✓ passed 0ms
Given
three Case records of one Parametrized scenario
When
the grouping pass collapses them
Then
one scenario remains and any failed Case fails it
The grouped tree comes from the first passed case
parametrization ✓ passed 0ms
Given
a skipped first Case and a second one that ran
When
the cases are grouped
Then
the tree is the one the passed Case recorded
A plain-str narration that varies across cases is refused
parametrization validation ✓ passed 0ms
Given
two cases whose text differs but records no parts
When
the cases are grouped
Then
the grouping is refused
the error names the test, the missing parts and the t-string fix
A narrated value that varies becomes a derived parameter table column
parametrization ✓ passed 0ms
Given
two cases narrating a value that differs
When
templatizing walks the cases
Then
the value becomes a derived column beside the parametrize one
the Step keeps a placeholder pointing at that column
A varying interpolation that is not a bare name is refused
diagnostics parametrization validation ✓ passed 0ms
Given
two cases narrating a computed expression
When
the cases are grouped
Then
the grouping is refused
the error quotes the expression and shows the bind-a-local fix
A parameter table cell reads the way the scenario name formats it
parametrization ✓ passed 0ms
Given
a Template scenario name formatting its parameter
When
the cases are grouped
Then
the cells carry the formatting the name declared
A scenario name formatting a parameter a step reads plainly gets its own column
parametrization ✓ passed 0ms
Given
a name formatting the parameter and a step reading it plainly
When
the cases are grouped
Then
the name points at a column holding what it renders
the name renders the disambiguated token, text and parts agreeing
A step formatting a parameter the scenario name reads plainly gets its own column
parametrization ✓ passed 0ms
Given
a step formatting the parameter and a name reading it plainly
When
the cases are grouped
Then
the step points at a column holding what it renders
the step renders the disambiguated token, text and parts agreeing
A step narrating a parameter its column no longer holds is refused
parametrization validation ✓ passed 0ms
Given
two cases narrating a value their column lacks
When
the cases are grouped
Then
the grouping is refused
the error names the column and what the case actually narrated
A term ref whose display differs between cases is refused
parametrization validation ✓ passed 0ms
Given
two cases whose Term ref reads differently
When
the cases are grouped
Then
the grouping is refused
the error names the Term ref and the split-it-out fix
A term ref that *is* the parametrize value is refused too
parametrization validation ✓ passed 0ms
Given
two cases whose Term ref is the parameter itself
When
the cases are grouped
Then
the grouping is refused
the error points at the per-case scenario opt-out
An attachment whose payload varies becomes an attachment column
parametrization ✓ passed 0ms
Given
two cases attaching a label with differing payloads
When
templatizing walks the cases
Then
the payload moves into an attachment column
the Step keeps a content-less badge pointing at it
A step whose set of attachment labels differs between cases is refused
parametrization validation ✓ passed 0ms
Given
an Attachment label only one Case attaches
When
the cases are grouped
Then
the grouping is refused
the error names the label and asks for a constant one
A parameter table cell reads the way the step that points at it read
parametrization ✓ passed 0ms
Given
two cases narrating a parameter with a format spec
When
grouping builds the parameter table
Then
each cell carries the formatted text, under one column
the step keeps its placeholder, which that cell substitutes into
Cases that narrate different steps are refused rather than grouped
parametrization validation ✓ passed 0ms
Given
two cases whose step trees differ
When
the cases are grouped
Then
the grouping is refused
the error names the divergence and the opt-out that answers it
A step narrating a glossary term parameter keeps pointing at its parameter table column
parametrization ✓ passed 0ms
Given
a step narrating a parameter bound to a glossary term instance
When
the cases are grouped
Then
the parameter table holds the term displays alone
the step still points at that column
A parametrized scenario can decline the grouping and keep one scenario per case
parametrization ✓ passed 0ms
Given
two cases of a scenario that opted out
When
the grouping pass runs
Then
each case stands alone, with no parameter table
Term ids are derived as URL-safe slugs
✓ passed 0ms
Given
the name {text}
When
it is slugified into a Term id
Then
the id is the expected slug {expected}
text expected status
Guest 'guest'
Order received 'order-received'
Work Object 'work-object'
do_the_thing 'do-the-thing'
Buy / sell 'buy-sell'
Guest #1 'guest-1'
café 'caf'
booking system 'booking-system'
A name with no id-able characters is rejected
validation ✓ passed 0ms
Given
the name {text}
When
it is slugified into a Term id
Then
a PytestGivenError reports the derived id is empty
text status
---
###
A cross-phase step cannot open inside a when_then body
validation ✓ passed 0ms
Given
an Active scenario in a local Collector
When
a given or then opens inside the when_then body
Then
a PytestGivenError reports the cross-phase nesting
the Step stack is left balanced
phase_name status
given
then
An attachment label must be plain text
validation ✓ passed 0ms
Given
a non-str Attachment label of kind {label_kind}
When
it is attached
Then
a PytestGivenError says Attachment labels are plain text
label_kind status
deferred-template
t-string
not-a-string
A Template accepts bare identifiers only
validation ✓ passed 0ms
Given
the placeholder {text}
When
a Templatize template is built from it
Then
a PytestGivenError says bare identifiers only
text status
count={obj.attr}
{d[key]}
{x + 1}
Adopt pytest-given
11 activities · 22 scenarios
1
Domain ExperttellsStoryto theDeveloper
no coverage
2
DevelopercapturesStoryasActivity
3 scenarios
3
DeveloperbuildsGlossarywith theDomain Expert
3 scenarios
4
AgentwritesScenariowithTagagainst theGlossary
1 scenario
5
AgentnarratesStepwith aPhase
3 scenarios
6
AgentattachesAttachmentto aStep
1 scenario
7
CollectorrecordsStepon theStep stack
3 scenarios
8
CollectorgraftsFixture recordingfrom aStep fixture
3 scenarios
9
CollectorgroupsParametrized scenariointo aParameter table
2 scenarios
10
RendererrendersReportwithParameter coloring
1 scenario
11
Domain ExpertreviewsScenarioin theReport
no coverage
Scenarios
Steps record with their phases
✓ passed
Covers: 7
Steps pushed during fixture setup record into the fixture recording
✓ passed
Covers: 7
An attachment lands on the step being recorded
✓ passed
Covers: 6
Fixture-body steps do not leak into the active scenario
✓ passed
Covers: 7
A fixture recording is deep-copied when grafted
✓ passed
Covers: 8
A leaf given is grafted as a childless given step
✓ passed
Covers: 8
Grafting with an override replaces the root label but keeps children
✓ passed
Covers: 8
Registering an actor returns a typed handle
✓ passed
Covers: 3
Re-registering a term with matching fields is idempotent
✓ passed
Covers: 3
Calling the glossary declares a kindless term
✓ passed
Covers: 3
A step pairs its narration with a phase
✓ passed
Covers: 5
when_then records the action and its outcome as siblings
✓ passed
Covers: 5
A nested when becomes a child of the when_then action
✓ passed
Covers: 5
A single-path activity synthesizes one path
✓ passed
Covers: 2
An activity may branch into multiple paths
✓ passed
Covers: 2
A story auto-numbers its activities from one
✓ passed
Covers: 2
A FileGlossary handle works in a t-string step
✓ passed
Covers: 4
Parameter coloring marks placeholders and table headers
✓ passed
Covers: 10
Grouping collapses parametrize cases into one scenario
✓ passed
Covers: 9
A parameter table cell reads the way the step that points at it read
✓ passed
Cases that narrate different steps are refused rather than grouped
✓ passed
A parametrized scenario can decline the grouping and keep one scenario per case
✓ passed
Covers: 9
Glossary
48 terms · 5 actors · 15 work objects · 2 verbs · 26 uncategorized
Actors 5 terms
Collector
The module-level singleton that accumulates scenarios, fixture recordings, and parameter info during a pytest session. Reset at the start of each session.
1 story · 9 scenarios
Renderer
Converts a JSON report into a rendering — a self-contained HTML page or a Markdown document.
1 story · 1 scenario
Developer
Person who writes the application code and — together with the Agent — the scenarios; curates the glossary with the Domain Expert.
1 story
Domain Expert
Person who owns the domain knowledge and the ubiquitous language; source of domain stories and reviewer of scenarios and reports. A stakeholder in the broad sense.
1 story
Agent
AI coding agent that authors and maintains scenarios alongside the Developer, guided by the pytest-given skills.
1 story
Work Objects 15 terms
Scenario
A test function decorated with @scenario(...). Not every pytest test is a scenario — undecorated tests are tolerated but not collected.
1 instance · 1 story · 18 scenarios
Step
A unit of narration: a with given(...) / when(...) / then(...) block, or the root recording from a step fixture. Steps nest. Each carries a phase, narration, attachments, and children — but no status and no error of its own: a failure is recorded on the Scenario, and per Case on the Parameter table, which is where both renderers read it.
1 instance · 1 story · 36 scenarios
Phase
The category of a step: given, when, or then. A step has exactly one phase.
1 story · 2 scenarios
Tag
Free-form string label attached via @scenario(name, tags=[...]). Used by the report's filter UI.
1 story · 1 scenario
Attachment
A labeled blob (text or JSON) bound to the currently-active step via attach(label, content).
1 instance · 1 story · 6 scenarios
Parametrized scenario
A @scenario-decorated test that also carries @pytest.mark.parametrize(...). Produces multiple scenario records during a run; pytest-given groups them.
1 story · 4 scenarios
Parameter table
The per-scenario grouping of typed columns + cases. A column is a param (a @pytest.mark.parametrize input), a derived (a narrated value that varies across cases) or an attachment (an attachment whose payload varies). Appears in the report below the grouped-template steps.
1 instance · 1 story · 8 scenarios
Step fixture
A pytest fixture whose function is wrapped with @given(text). Only @given is allowed on fixtures; @when / @then are rejected.
1 story · 2 scenarios
Fixture recording
A captured subtree of steps + attachments produced while a step fixture is being set up (and, for generator fixtures, torn down). Stored keyed by fixture-instance identity.
1 story · 5 scenarios
Step stack
The chain of currently-open steps; entered by with given(...), popped on exit. Mirrored inside a fixture recording while a fixture body is running.
1 story · 1 scenario
Report
The output artifact: a JSON data file and optional self-contained HTML and Markdown renderings derived from it. The JSON is the source of truth.
1 story · 9 scenarios
Parameter coloring
Each parametrize column gets a stable highlight color; placeholders and matching values share that color wherever they appear in step text and the parameter table.
1 story · 1 scenario
Glossary
The Ubiquitous-Language concept — the shared vocabulary a domain speaks in — and the class that realizes it: Glossary(), with .actor(...), .work_object(...), .verb(...) registration methods and g('foo') (declare-or-get a kindless term, optional definition=) / g['foo'] (get-only, raises on unknown) accessor forms.
1 story · 10 scenarios
Story
A named flow modeled as a sequence of activities. Constructed by story('Title', [activity(...), ...]). Stories are first-class report tabs and the unit of coverage.
1 story · 26 scenarios
Activity
One row in a story — typically actor + verb + work_object plus optional connective words. Constructed by activity(...).
2 instances · 1 story · 24 scenarios
Verbs 2 terms
Group
Collapsing the N scenario records of a parametrized scenario into one logical scenario carrying a Parameter table. Cases group when they share the same Node ID without its parametrize tail — one test function — and the same name; two same-named scenarios on different test functions stay separate. @scenario(group_parametrized=False) declines the merge, so each case lives on as its own scenario.
1 story · 15 scenarios
Graft
Attaching a fixture recording into the active scenario's step tree at the moment its host test starts.
1 story · 4 scenarios
Uncategorized 26 terms
Narration
The human-readable text on a step or scenario name. Modeled as a Narration dataclass bundling a flat rendered text: str with a parts: list[NarrationPart] (empty for plain-string authoring; populated when the source was a t-string or pytest_given.Template, with NarrationLiteral / NarrationValue / NarrationPlaceholder / NarrationTermRef pieces). The structured form lets the templatizer and renderer treat parametrize-bound values specially without regex tricks.
6 scenarios
when_then
A step-authoring helper that emits a when action and its then outcome as two sibling steps from a single with block. Used mainly to narrate an expected raise (with when_then('the action', 'the error is raised'), pytest.raises(...)), so the action and its outcome stay distinct steps.
5 scenarios
Case
One row of a parametrized scenario — a single tuple of parameter values, its status, and any error.
19 scenarios
Templatize
Derive the grouped-template step text by comparing every comparable case: what all of them share stays inline, and anything that varies becomes a {name} placeholder or attachment badge pointing at a parameter-table column. The baseline tree comes from the first passed case, and every other passed case must narrate that same template.
5 scenarios
Plain fixture
A pytest fixture without a pytest-given decorator. Used by tests but produces no step in the report.
Active scenario
The scenario currently being recorded into; tracked by node ID.
11 scenarios
Node ID
A pytest test identifier (e.g., tests/test_x.py::test_y[a-b]). Used as a key throughout the collector.
2 scenarios
Value highlight
A neutral highlight applied to t-string interpolation values that don't correspond to a parametrize column and are constant across every case (e.g., a computed expression like price * 1.2). One that varies becomes a derived column instead.
1 scenario
Source link
A clickable file:line anchor on a scenario card, a story panel, or an expanded glossary term card. Resolved from the given_source_link config (preset name like vscode / github, or a raw URL template). Captured as a SourceLocation (POSIX relpath + 1-indexed line) — from pytest.Item.location for a scenario, from the declaration site for a Story or Term. Disabled by default.
1 scenario
File glossary
A glossary loaded from a Markdown file, via the FileGlossary(path) class. It parses all GFM pipe tables in the file into the same inner Glossary model; terms are accessed by name (g['Guest'], case-insensitive). Kind inference fills in term kinds post-collection from activity slot positions when no explicit kind_column is configured.
21 scenarios
Deferred term
A term obtained before its kind is known — the DeferredTermHandle returned by the code glossary (g('foo') declare-or-get, g['foo'] get-only) and by a file glossary (g['Guest']). One handle type serves all kinds (unlike the eager Actor/Work Object/Verb handles); the kind stays None until kind inference runs.
2 scenarios
Term
A registered glossary entry: an Actor, Work Object, Verb, or kindless term. Each carries an id (slug), a canonical name, a kind (None when kindless), and an optional definition (`str
48 scenarios
Actor
A glossary term for a participant in the domain (e.g., Guest). Carries the actor kind color: a wash in narration, a pill in the Glossary view.
27 scenarios
Work Object
A glossary term for a thing acted on (e.g., Room, Booking). Carries the work-object kind color: a wash in narration, a pill in the Glossary view.
12 scenarios
Verb
A glossary term for an action (e.g., book, confirm). Verbs accept inflections — calling book('books') records books as a surface form of the canonical book.
27 scenarios
Term ref
An occurrence of a term inside narration. Modeled as NarrationTermRef in step text and as ActivityTermRef inside activity prose.
20 scenarios
Instance
A named refinement of an Actor or Work Object (e.g., guest('Alice') is an instance of the Guest actor). Instances aggregate in the Glossary tab's refs block.
10 scenarios
Inflection
A surface form of a Verb other than its canonical name (e.g., searches for as an inflection of search). Reported under "Also used as:" in the Glossary.
9 scenarios
Activity Part
The two-variant union making up an activity's prose (ActivityPart = `ActivityTermRef
4 scenarios
Path
A branching segment inside a story — path(...) lets alternate activity sequences share a prefix.
19 scenarios
Slot
A position role in an activity path, from its node/edge alternation: position 0 is the actor slot, odd positions are verb slots, and even positions ≥ 2 are noun slots. Slots drive both path validation and kind inference.
14 scenarios
Scenario↔activity binding
The link between a scenario (or step) and one or more story activities. Carried by @scenario(story=, activities=) and the activity= kwarg on given/when/then.
3 scenarios
Coverage
The "did this scenario touch that activity" relation. Computed by the A_refs ⊆ S rule: an activity is covered when its set of term references (as identities, with a canonical fallback) is a subset of a single step's term-reference identities — matching is per step, not against the union across steps. A step can also cover an activity explicitly via the activity= pin, regardless of its narration.
16 scenarios
Kind inference
The post-collection pass (infer_glossary_kinds) that assigns each undeclared term a kind from the slot positions it occupies across all story activities; declared kinds are verified against observed positions instead. A term used in incompatible slots (or conflicting with its declared kind) raises. A term never referenced by any activity stays kindless.
4 scenarios
Kindless
A term with kind=None — left unset when kind inference finds no story slot to infer from (a term used only in t-string steps, never in an activity). Shown in the report's Uncategorized bucket.
10 scenarios
Undefined
A term with definition is None; surfaced by a badge and filter in the Glossary view. Orthogonal to kindless.
1 scenario