Status: Draft (a translation of the paper’s design into a product operation overview. Some parts, such as the 3 commands under UC5, are already implemented in the CLI) Related documents: A Git-Native Model for Test Knowledge Management: Integrated Edition (the paper this document translates), cli-manual.md (implementation details of the commands corresponding to each UC)
Positioning: This document translates the design of the paper “A Git-Native Model for Test Knowledge Management: Integrated Edition” (git-native-model-for-test-knowledge-management.md) into the operational picture of actually running it as a product. Where a point is explicitly stated in the body of the paper, the corresponding section and line number are noted; where a point was supplemented for productization, it is explicitly marked “(productization proposal, not stated in the paper body)”.
sequenceDiagram
actor TD as Test Designer
participant KN as .markharness/knowledge/**
actor RM as Release Manager
participant CI as CI Bot
participant GEN as .markharness/generated/testcases/*.yml
participant CH as .markharness/changes/milestone.yaml
participant BF as Backfill Worker
participant NOTES as git notes
TD->>KN: Write feature.yml
TD->>KN: Write condition.yml
TD->>KN: Write expected/*.yml
TD->>KN: Manually write forked_from (only when there is a conceptual derivation, §3.1)
TD->>CI: Create PR
CI->>GEN: Deterministically regenerate TestCase from Feature+Condition
CI->>CI: Verify that the regenerated result matches the current files (§4.5)
alt Diff exists
CI-->>TD: Request diff review (human judgment point)
else No diff
CI-->>TD: Verification OK
end
RM->>KN: Assign milestone tag (human judgment point)
RM->>CI: Notify of tag push
CI->>CH: Compute derived_from by comparing the Feature directory tree SHA between the two most recent milestones (§3.2-3.4, core contribution)
CI->>NOTES: Record "lineage computation for this milestone is complete" (§4.3)
par Asynchronous background
BF->>NOTES: Check unprocessed past milestones
BF->>CH: Backfill past .markharness/changes/*.yaml as a low-priority job (§4.1-4.2)
BF->>NOTES: Record processed milestones
end
Note over CI,CH: Any interval whose backfill is not yet complete at query time is computed on demand and cached (§4.4)
Key points of the creation order
.markharness/knowledge/**/feature.yml → condition.yml → expected/*.yml (manually written by the Test Designer).markharness/generated/testcases/*.yml (deterministically generated by CI, and verified to match the existing files).markharness/changes/<milestone>.yaml (CI automatically computes derived_from)git notes → delayed backfilling of past milestones (asynchronous, automatic)Since mermaid has no UML use case diagram, actors are represented as nodes and use cases as rounded nodes (a visual substitute).
flowchart LR
subgraph Actors
TD[Test Designer]
RV[Reviewer]
RM[Release Manager]
CIBOT((CI Bot))
BFW((Backfill Worker))
MIG[Data Migration Operator]
end
subgraph UseCases
UC1(("UC1: Write knowledge"))
UC1b(("UC1b: Manually write forked_from"))
UC2(("UC2: Deterministically generate TestCase"))
UC3(("UC3: Review and merge generated artifacts"))
UC4(("UC4: Tag a milestone"))
UC5(("UC5: Automatically compute ChangeEvent"))
UC6(("UC6: Run backfill asynchronously"))
UC7(("UC7: Discard and rebuild the id cache"))
UC8(("UC8: Import from an existing tool"))
end
TD --> UC1
TD --> UC1b
TD --> UC7
RV --> UC3
RM --> UC4
CIBOT --> UC2
CIBOT --> UC5
CIBOT --> UC7
BFW --> UC6
MIG --> UC8
UC1 -.include.-> UC2
UC2 -.include.-> UC3
UC4 -.include.-> UC5
UC5 -.include.-> UC6
| # | Use case | Actor | Trigger | Precondition | Main flow | Postcondition | Human involvement |
|---|---|---|---|---|---|---|---|
| UC1 | Write knowledge | Test Designer | Addition of a new feature/new condition | None | Create and commit feature.yml/condition.yml/expected/*.yml |
.markharness/knowledge/ is updated |
Manually written (§3.1, line 108) |
| UC1b | Manually write forked_from | Test Designer | A conceptual derivation from another Feature occurs | The source Feature exists | Write the source id into the forked_from field |
Domain knowledge that does not appear in Git history is made explicit | Mandatory manual entry (cannot be automatically derived from Git history, line 153) |
| UC2 | Deterministically generate TestCase | CI Bot | PR creation/push | feature.yml/condition.yml exist |
Mechanically scan the Feature+Condition pairs and regenerate .markharness/generated/testcases/*.yml |
The generated artifacts come to match the latest knowledge | Automatic (no human intervention). However, the diff-verification result against existing files is presented to a human (§4.5, line 316) |
| UC3 | Review and merge generated artifacts | Reviewer | UC2 complete, diff detected | CI has detected a diff | Review the diff content, judge whether it is an intended change, and merge | .markharness/generated/testcases/*.yml is finalized and integrated into main |
Human judgment point: the final gate that prevents unintended changes from slipping in |
| UC4 | Tag a milestone | Release Manager | Release decision | The main branch is stable | Run git tag <milestone> |
The milestone boundary is finalized | Human judgment point: the release-timing decision itself (Figure 3) |
| UC5 | Automatically compute ChangeEvent | CI Bot | Tag push | A tag for the immediately preceding milestone exists | Between the two milestones, compare the Feature directory tree SHA for each id resolved via the id resolution, compute derived_from, and write it to .markharness/changes/<milestone>.yaml |
The version history (ChangeEvent) is generated | Automatic (core contribution, §3.2-3.4). change_type is not written; a human fills it in afterward, as described in Supplement 6 below |
| UC6 | Run backfill asynchronously | Backfill Worker | UC5 complete, or a query against an unprocessed interval | An unprocessed interval exists in git notes |
Compute the past lineage starting with priority given to the most recent milestone, recording to git notes upon completion of each |
.markharness/changes/*.yaml for past milestones is filled in incrementally |
Automatic. However, the operator can configure processing-priority adjustments (productization proposal, not stated in the paper body) |
| UC7 | Discard and rebuild the id cache | Test Designer / CI Bot | Suspected cache inconsistency | An id-resolution cache exists | Run the --no-cache option or the rebuild command |
The cache is rebuilt | Explicit manual discard (fail-safe, line 199) |
| UC8 | Import from an existing tool | Data Migration Operator | Migration of existing TestRail/Xray/TestLink assets, or of existing Gherkin (.feature) assets |
An export file, or .feature files, have been prepared |
Run the importer to convert to this format (the .markharness/knowledge/ structure) |
Existing assets are reflected under .markharness/knowledge/ |
Manual trigger (the migration work itself is carried out by a human, §4.5). See the supplement below for the scope of Gherkin import |
preconditions/steps/additional_preconditions/additional_steps/results fields decisions/0016 added to Behavior/Condition/ExpectedResult make most of a plain Feature + Background + Scenario + Given/When/Then (Feature → Behavior, Background → behavior.preconditions, Scenario → Condition, a scenario-leading Given → condition.additional_preconditions, When → condition.steps, Then → expected_result.results, an extra When/Then pair → another expected/*.yml’s additional_steps/results) representable. The integration is designed as two independent one-way features, not a bidirectional round trip:
.feature file into markharness YAML through human review (a flow where a human inspects and edits the Knowledge Intent before it is reconciled). Syntax with no structural home in markharness’s semantic model — Scenario Outline + Examples (the same open question as the examples: field decisions/0016 left out of scope), Data Tables/Doc Strings inside Given/When/Then steps (every field in this format is a Vec<String>, so there is no home for structured data besides flattening to a plain string), the Rule: keyword (there is no layer in the Requirement → Feature → Behavior → Condition hierarchy that corresponds to it), the relationship between tags (@tag) and axis (a tag is a free label, while axis must be pre-registered in axes/*.yml — not a simple 1:1 conversion), the non-canonical case where a Given appears after When/Then within a Scenario (condition.additional_preconditions has only one slot per Condition), and a Scenario with no When step (incompatible with condition.steps’s minItems: 1) — is not auto-converted; the conversion tool warns the human and asks for manual handling instead (since this is always a human-supervised conversion, an unconditional lossless auto-conversion is not required). Whether this conversion is used as a one-time migration, or the .feature file keeps being edited and re-converted repeatedly, is left to the user’s own operating practice, not prescribed here. For traceability, the behavior.yml/condition.yml produced by the conversion carries a source field (trace-only, not consumed by generation) recording the original .feature file’s path and the target Scenario name (Feature name, for a Behavior). When the same .feature file is converted again, this source can also serve as the matching key so the tool updates the existing Condition/Behavior under its existing uid instead of minting a new one (needed only if the operating practice chooses repeated conversion; details left to implementation time)..feature files from TestCase, following the same “generated artifact (committed, regeneration-match verified by CI)” pattern as generated/testcases/*.yml.The main flow of UC4 (running git tag <milestone>) itself remains unchanged as a human judgment point, but two commands have been implemented to assist the subsequent mechanical work of “recording execution results into .markharness/executions/” (docs/cli-manual.md §1.13/1.14).
markharness milestone init <tag>: creates .markharness/executions/<tag>/milestone.yml corresponding to an existing git tag. It only verifies the tag’s existence and does not perform the tagging decision itself.markharness execution record <case_id> --milestone <name> --result <pass|fail|skip> --executor <name>: appends a TestCase execution result to .markharness/executions/<milestone>/results.yml through a common interface, whether triggered from CI or from QA.The main flow of UC5 (markharness changes compute) itself is unchanged, but three commands have been implemented to assist the parts that §3.2 and §3.5 of the paper position as “entered by a human afterward” or “an auxiliary function for auditing” (docs/cli-manual.md §1.15-1.17).
markharness changes annotate <event_id> --type <spec-change|bug-fix|refactor|other>: lets a human set, after the fact, the change_type (§3.5) that changes compute generates as a blank field. Because it searches across .markharness/changes/ by event_id, there is no need to know in advance which milestone-interval file the target belongs to.markharness changes lineage --commit <merge-commit-sha>: an audit-only command that compares the two parents of the specified merge commit against the merge base obtained via git merge-base, and determines for each Feature id whether it is linear/a true divergence/equivalent to a single parent (§3.2). It does not write to the main lineage (.markharness/changes/*.yaml) produced by changes compute.markharness validate: structurally validates .markharness/knowledge//.markharness/axes/ against .markharness/schema/*.schema.json (the default set placed by markharness init), and additionally checks whether axis tags are registered and whether the reference targets of forked_from exist (the implementation of §3.5’s “use schema validation to prevent front matter from using values not defined in axes/*.yml”).