diff --git a/specification.json b/specification.json index 2f4edfd6..e86e416b 100644 --- a/specification.json +++ b/specification.json @@ -8,9 +8,23 @@ "children": [] }, { - "id": "Requirement 1.1.2", - "machine_id": "requirement_1_1_2", - "content": "The `API` MUST provide a function to set the default `provider`, which accepts an API-conformant `provider` implementation.", + "id": "Requirement 1.1.2.1", + "machine_id": "requirement_1_1_2_1", + "content": "The `API` MUST define a `provider mutator`, a function to set the default `provider`, which accepts an API-conformant `provider` implementation.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 1.1.2.2", + "machine_id": "requirement_1_1_2_2", + "content": "The `provider mutator` function MUST invoke the `initialize` function on the newly registered provider before using it to resolve flag values.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 1.1.2.3", + "machine_id": "requirement_1_1_2_3", + "content": "The `provider mutator` function MUST invoke the `shutdown` function on the previously registered provider once it's no longer being used to resolve flag values.", "RFC 2119 keyword": "MUST", "children": [] }, @@ -213,6 +227,13 @@ "RFC 2119 keyword": "MUST", "children": [] }, + { + "id": "Requirement 1.6.1", + "machine_id": "requirement_1_6_1", + "content": "The API MUST define a `shutdown` function, which, when called, must call the respective `shutdown` function on the active provider.", + "RFC 2119 keyword": "MUST", + "children": [] + }, { "id": "Requirement 2.1.1", "machine_id": "requirement_2_1_1", @@ -289,23 +310,23 @@ "content": "The `resolution details` structure SHOULD accept a generic argument (or use an equivalent language feature) which indicates the type of the wrapped `value` field.", "RFC 2119 keyword": "SHOULD", "children": [] + }, + { + "id": "Requirement 2.2.9", + "machine_id": "requirement_2_2_9", + "content": "The `provider` SHOULD populate the `resolution details` structure's `flag metadata` field.", + "RFC 2119 keyword": "SHOULD", + "children": [] + }, + { + "id": "Requirement 2.2.10", + "machine_id": "requirement_2_2_10", + "content": "`flag metadata` MUST be a structure supporting the definition of arbitrary properties, with keys of type `string`, and values of type `boolean | string | number`.", + "RFC 2119 keyword": "MUST", + "children": [] } ] }, - { - "id": "Requirement 2.2.9", - "machine_id": "requirement_2_2_9", - "content": "The `provider` SHOULD populate the `resolution details` structure's `flag metadata` field.", - "RFC 2119 keyword": "SHOULD", - "children": [] - }, - { - "id": "Requirement 2.2.10", - "machine_id": "requirement_2_2_10", - "content": "`flag metadata` MUST be a structure supporting the definition of arbitrary properties, with keys of type `string`, and values of type `boolean | string | number`.", - "RFC 2119 keyword": "MUST", - "children": [] - }, { "id": "Requirement 2.3.1", "machine_id": "requirement_2_3_1", @@ -327,6 +348,48 @@ "RFC 2119 keyword": "MAY", "children": [] }, + { + "id": "Requirement 2.4.1", + "machine_id": "requirement_2_4_1", + "content": "The `provider` MAY define an `initialize` function which accepts the global `evaluation context` as an argument and performs initialization logic relevant to the provider.", + "RFC 2119 keyword": "MAY", + "children": [] + }, + { + "id": "Requirement 2.4.2", + "machine_id": "requirement_2_4_2", + "content": "The `provider` MAY define a `status` field/accessor which indicates the readiness of the provider, with possible values `NOT_READY`, `READY`, or `ERROR`.", + "RFC 2119 keyword": "MAY", + "children": [] + }, + { + "id": "Requirement 2.4.3", + "machine_id": "requirement_2_4_3", + "content": "The provider MUST set its `status` field/accessor to `READY` if its `initialize` function terminates normally.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 2.4.4", + "machine_id": "requirement_2_4_4", + "content": "The provider MUST set its `status` field to `ERROR` if its `initialize` function terminates abnormally.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 2.4.5", + "machine_id": "requirement_2_4_5", + "content": "The provider SHOULD indicate an error if flag resolution is attempted before the provider is ready.", + "RFC 2119 keyword": "SHOULD", + "children": [] + }, + { + "id": "Requirement 2.5.1", + "machine_id": "requirement_2_5_1", + "content": "The provider MAY define a `shutdown` function to perform whatever cleanup is necessary for the implementation.", + "RFC 2119 keyword": "MAY", + "children": [] + }, { "id": "Requirement 3.1.1", "machine_id": "requirement_3_1_1", diff --git a/specification/sections/01-flag-evaluation.md b/specification/sections/01-flag-evaluation.md index c9932bb1..47421e9d 100644 --- a/specification/sections/01-flag-evaluation.md +++ b/specification/sections/01-flag-evaluation.md @@ -20,9 +20,11 @@ The `evaluation API` allows for the evaluation of feature flag values, independe It's important that multiple instances of the `API` not be active, so that state stored therein, such as the registered `provider`, static global `evaluation context`, and globally configured `hooks` allow the `API` to behave predictably. This can be difficult in some runtimes or languages, but implementors should make their best effort to ensure that only a single instance of the `API` is used. -#### Requirement 1.1.2 +### Setting a provider -> The `API` **MUST** provide a function to set the default `provider`, which accepts an API-conformant `provider` implementation. +#### Requirement 1.1.2.1 + +> The `API` **MUST** define a `provider mutator`, a function to set the default `provider`, which accepts an API-conformant `provider` implementation. ```typescript // example provider mutator @@ -33,6 +35,22 @@ This provider is used if there is not a more specific client name binding. (see See [provider](./02-providers.md) for details. +#### Requirement 1.1.2.2 + +> The `provider mutator` function **MUST** invoke the `initialize` function on the newly registered provider before using it to resolve flag values. + +The `provider's` readiness can state can be determined from its `status` member/accessor. + +See [provider initialization](./02-providers.md#24-initialization). + +#### Requirement 1.1.2.3 + +> The `provider mutator` function **MUST** invoke the `shutdown` function on the previously registered provider once it's no longer being used to resolve flag values. + +Setting a new provider means the previous provider is no longer in use, and should therefore be disposed of using its `shutdown` function. + +see: [shutdown](./02-providers.md#26-shutdown), [setting a provider](#setting-a-provider) + #### Requirement 1.1.3 > The `API` **MUST** provide a function to bind a given `provider` to one or more client `name`s. If the client-name already has a bound provider, it is overwritten with the new mapping. @@ -105,11 +123,11 @@ See [hooks](./04-hooks.md) for details. client.getMetadata().getName(); // "my-client" ``` -#### 1.3. Flag Evaluation +### 1.3. Flag Evaluation [![hardening](https://img.shields.io/static/v1?label=Status&message=hardening&color=yellow)](https://github.com/open-feature/spec/tree/main/specification#hardening) -##### Requirement 1.3.1 +#### Requirement 1.3.1 > The `client` **MUST** provide methods for typed flag evaluation, including boolean, numeric, string, and structure, with parameters `flag key` (string, required), `default value` (boolean | number | string | structure, required), `evaluation context` (optional), and `evaluation options` (optional), which returns the flag value. @@ -129,32 +147,31 @@ MyStruct myStruct = client.getObjectValue('structured-flag', { text: ' See [evaluation context](./03-evaluation-context.md) for details. -##### Condition 1.3.2 +#### Condition 1.3.2 > The implementation language differentiates between floating-point numbers and integers. -###### Conditional Requirement 1.3.2.1 +##### Conditional Requirement 1.3.2.1 > The client **SHOULD** provide functions for floating-point numbers and integers, consistent with language idioms. -```go -// example in GO -GetIntValue(flag string, defaultValue int64, evalCtx EvaluationContext, options ...EvaluationOption) (int64, error) +```java +int getIntValue(String flag, int defaultValue); -GetFloatValue(flag string, defaultValue float64, evalCtx EvaluationContext, options ...EvaluationOption) (float64, error) +long getFloatValue(String flag, long defaultValue); ``` See [types](../types.md) for details. -##### Requirement 1.3.3 +#### Requirement 1.3.3 > The `client` **SHOULD** guarantee the returned value of any typed flag evaluation method is of the expected type. If the value returned by the underlying provider implementation does not match the expected type, it's to be considered abnormal execution, and the supplied `default value` should be returned. -#### 1.4. Detailed Flag Evaluation +### 1.4. Detailed Flag Evaluation [![hardening](https://img.shields.io/static/v1?label=Status&message=hardening&color=yellow)](https://github.com/open-feature/spec/tree/main/specification#hardening) -##### Requirement 1.4.1 +#### Requirement 1.4.1 > The `client` **MUST** provide methods for detailed flag value evaluation with parameters `flag key` (string, required), `default value` (boolean | number | string | structure, required), `evaluation context` (optional), and `evaluation options` (optional), which returns an `evaluation details` structure. @@ -173,80 +190,93 @@ FlagEvaluationDetails myStructDetails = client.getObjectDetails The `evaluation details` structure's `value` field **MUST** contain the evaluated flag value. -##### Condition 1.4.3 +#### Condition 1.4.3 > The language supports generics (or an equivalent feature). -###### Conditional Requirement 1.4.3.1 +##### Conditional Requirement 1.4.3.1 > The `evaluation details` structure **SHOULD** accept a generic argument (or use an equivalent language feature) which indicates the type of the wrapped `value` field. -##### Requirement 1.4.4 +#### Requirement 1.4.4 > The `evaluation details` structure's `flag key` field **MUST** contain the `flag key` argument passed to the detailed flag evaluation method. -##### Requirement 1.4.5 +#### Requirement 1.4.5 > In cases of normal execution, the `evaluation details` structure's `variant` field **MUST** contain the value of the `variant` field in the `flag resolution` structure returned by the configured `provider`, if the field is set. -##### Requirement 1.4.6 +#### Requirement 1.4.6 > In cases of normal execution, the `evaluation details` structure's `reason` field **MUST** contain the value of the `reason` field in the `flag resolution` structure returned by the configured `provider`, if the field is set. -##### Requirement 1.4.7 +#### Requirement 1.4.7 > In cases of abnormal execution, the `evaluation details` structure's `error code` field **MUST** contain an `error code`. See [error code](../types.md#error-code) for details. -##### Requirement 1.4.8 +#### Requirement 1.4.8 > In cases of abnormal execution (network failure, unhandled error, etc) the `reason` field in the `evaluation details` **SHOULD** indicate an error. -##### Requirement 1.4.9 +#### Requirement 1.4.9 > Methods, functions, or operations on the client **MUST NOT** throw exceptions, or otherwise abnormally terminate. Flag evaluation calls must always return the `default value` in the event of abnormal execution. Exceptions include functions or methods for the purposes for configuration or setup. Configuration code includes code to set the provider, instantiate providers, and configure the global API object. -##### Requirement 1.4.10 +#### Requirement 1.4.10 > In the case of abnormal execution, the client **SHOULD** log an informative error message. Implementations may define a standard logging interface that can be supplied as an optional argument to the client creation function, which may wrap standard logging functionality of the implementation language. -##### Requirement 1.4.11 +#### Requirement 1.4.11 > The `client` **SHOULD** provide asynchronous or non-blocking mechanisms for flag evaluation. It's recommended to provide non-blocking mechanisms for flag evaluation, particularly in languages or environments wherein there's a single thread of execution. -##### Requirement 1.4.12 +#### Requirement 1.4.12 > In cases of abnormal execution, the `evaluation details` structure's `error message` field **MAY** contain a string containing additional details about the nature of the error. -##### Requirement 1.4.13 +#### Requirement 1.4.13 > If the `flag metadata` field in the `flag resolution` structure returned by the configured `provider` is set, the `evaluation details` structure's `flag metadata` field **MUST** contain that value. Otherwise, it **MUST** contain an empty record. This `flag metadata` field is intended as a mechanism for providers to surface additional information about a feature flag (or its evaluation) beyond what is defined within the OpenFeature spec itself. The primary consumer of this information is a provider-specific hook. -##### Condition 1.4.14 +#### Condition 1.4.14 > The implementation language supports a mechanism for marking data as immutable. -###### Conditional Requirement 1.4.14.1 +##### Conditional Requirement 1.4.14.1 > Condition: `Flag metadata` **MUST** be immutable. -#### Evaluation Options +### Evaluation Options -##### Requirement 1.5.1 +#### Requirement 1.5.1 > The `evaluation options` structure's `hooks` field denotes an ordered collection of hooks that the client **MUST** execute for the respective flag evaluation, in addition to those already configured. See [hooks](./04-hooks.md) for details. + +### 1.6. Shutdown + +[![experimental](https://img.shields.io/static/v1?label=Status&message=experimental&color=orange)](https://github.com/open-feature/spec/tree/main/specification#experimental) + +#### Requirement 1.6.1 + +> The API **MUST** define a `shutdown` function, which, when called, must call the respective `shutdown` function on the active provider. + +The precise name of this function is not prescribed by this specification, but should be defined be the SDK. +Relevant language idioms should be considered when choosing the name for this function, in accordance with the resource-disposal semantics of the language in question. + +see: [`shutdown`](./02-providers.md#25-shutdown) diff --git a/specification/sections/02-providers.md b/specification/sections/02-providers.md index bc1b0df2..da562118 100644 --- a/specification/sections/02-providers.md +++ b/specification/sections/02-providers.md @@ -1,6 +1,6 @@ --- title: Provider -description: The specification that defines the responsibilities and behaviors of a provider. +description: The specification that defines the interfaces, behaviors and responsibilities of providers. toc_max_heading_level: 4 --- @@ -30,7 +30,7 @@ provider.getMetadata().getName(); // "my-custom-provider" `Providers` are implementations of the `feature provider` interface, which may wrap vendor SDKs, REST API clients, or otherwise resolve flag values from the runtime environment. -##### Requirement 2.2.1 +#### Requirement 2.2.1 > The `feature provider` interface **MUST** define methods to resolve flag values, with parameters `flag key` (string, required), `default value` (boolean | number | string | structure, required) and `evaluation context` (optional), which returns a `resolution details` structure. @@ -41,11 +41,11 @@ resolveBooleanValue(flagKey, defaultValue, context); see: [flag resolution structure](../types.md#resolution-details), [flag value resolution](../glossary.md#resolving-flag-values) -##### Condition 2.2.2 +#### Condition 2.2.2 > The implementing language type system differentiates between strings, numbers, booleans and structures. -###### Conditional Requirement 2.2.2.1 +##### Conditional Requirement 2.2.2.1 > The `feature provider` interface **MUST** define methods for typed flag resolution, including boolean, numeric, string, and structure. @@ -63,11 +63,11 @@ ResolutionDetails resolveNumberValue(string flagKey, number defaultValue, contex ResolutionDetails resolveStructureValue(string flagKey, JsonObject defaultValue, context: EvaluationContext); ``` -##### Requirement 2.2.3 +#### Requirement 2.2.3 > In cases of normal execution, the `provider` **MUST** populate the `resolution details` structure's `value` field with the resolved flag value. -##### Requirement 2.2.4 +#### Requirement 2.2.4 > In cases of normal execution, the `provider` **SHOULD** populate the `resolution details` structure's `variant` field with a string identifier corresponding to the returned flag value. @@ -75,17 +75,17 @@ For example, the flag value might be `3.14159265359`, and the variant field's va The value of the variant field might only be meaningful in the context of the flag management system associated with the provider. For example, the variant may be a UUID corresponding to the variant in the flag management system, or an index corresponding to the variant in the flag management system. -##### Requirement 2.2.5 +#### Requirement 2.2.5 > The `provider` **SHOULD** populate the `resolution details` structure's `reason` field with `"STATIC"`, `"DEFAULT",` `"TARGETING_MATCH"`, `"SPLIT"`, `"CACHED"`, `"DISABLED"`, `"UNKNOWN"`, `"ERROR"` or some other string indicating the semantic reason for the returned flag value. As indicated in the definition of the [`resolution details`](../types.md#resolution-details) structure, the `reason` should be a string. This allows providers to reflect accurately why a flag was resolved to a particular value. -##### Requirement 2.2.6 +#### Requirement 2.2.6 > In cases of normal execution, the `provider` **MUST NOT** populate the `resolution details` structure's `error code` field, or otherwise must populate it with a null or falsy value. -##### Requirement 2.2.7 +#### Requirement 2.2.7 > In cases of abnormal execution, the `provider` **MUST** indicate an error using the idioms of the implementation language, with an associated `error code` and optional associated `error message`. @@ -98,11 +98,11 @@ See [error code](../types.md#error-code) for details. throw new ProviderError(ErrorCode.INVALID_CONTEXT, "The 'foo' attribute must be a string."); ``` -##### Condition 2.2.8 +#### Condition 2.2.8 > The implementation language supports generics (or an equivalent feature). -###### Conditional Requirement 2.2.8.1 +##### Conditional Requirement 2.2.8.1 > The `resolution details` structure **SHOULD** accept a generic argument (or use an equivalent language feature) which indicates the type of the wrapped `value` field. @@ -127,15 +127,15 @@ ResolutionDetails resolveStructureValue(string flagKey, MyStruct defau > `flag metadata` **MUST** be a structure supporting the definition of arbitrary properties, with keys of type `string`, and values of type `boolean | string | number`. -#### 2.3. Provider hooks +### 2.3. Provider hooks A `provider hook` exposes a mechanism for `provider authors` to register [`hooks`](./04-hooks.md) to tap into various stages of the flag evaluation lifecycle. These hooks can be used to perform side effects and mutate the context for purposes of the provider. Provider hooks are not configured or controlled by the `application author`. -##### Requirement 2.3.1 +#### Requirement 2.3.1 > The provider interface **MUST** define a `provider hook` mechanism which can be optionally implemented in order to add `hook` instances to the evaluation life-cycle. -``` +```java class MyProvider implements Provider { //... @@ -157,3 +157,98 @@ class MyProvider implements Provider { #### Requirement 2.3.3 > In cases of abnormal execution, the `resolution details` structure's `error message` field **MAY** contain a string containing additional detail about the nature of the error. + +### 2.4 Initialization + +[![experimental](https://img.shields.io/static/v1?label=Status&message=experimental&color=orange)](https://github.com/open-feature/spec/tree/main/specification#experimental) + +#### Requirement 2.4.1 + +> The `provider` **MAY** define an `initialize` function which accepts the global `evaluation context` as an argument and performs initialization logic relevant to the provider. + +Many feature flag frameworks or SDKs require some initialization before they can be used. +They might require the completion of an HTTP request, establishing persistent connections, or starting timers or worker threads. +The `initialization` function is an ideal place for such logic. + +```java +// MyProvider implementation of the initialize function defined in Provider +class MyProvider implements Provider { + //... + + // the global context is passed to the initialization function + void initialize(EvaluationContext initialContext) { + /* + A hypothetical initialization function: make an initial call doing some bulk initial evaluation, start a worker to do periodic updates + */ + this.flagCache = this.restClient.bulkEvaluate(initialContext); + this.startPolling(); + } + + //... +} +``` + +#### Requirement 2.4.2 + +> The `provider` **MAY** define a `status` field/accessor which indicates the readiness of the provider, with possible values `NOT_READY`, `READY`, or `ERROR`. + +Providers without this field can be assumed to be ready immediately. + +The diagram below illustrates the possible states and transitions of the `status` fields. + +```mermaid +--- +title: Provider State +--- +stateDiagram-v2 + direction LR + [*] --> NOT_READY + NOT_READY --> READY + READY --> ERROR + ERROR --> READY +``` + +see [provider status](../types.md#provider-status) + +#### Requirement 2.4.3 + +> The provider **MUST** set its `status` field/accessor to `READY` if its `initialize` function terminates normally. + +If the provider supports the `status` field/accessor and initialization succeeds, setting the `status` to `READY` indicates that the provider is initialized and flag evaluation is proceeding normally. + +#### Requirement 2.4.4 + +> The provider **MUST** set its `status` field to `ERROR` if its `initialize` function terminates abnormally. + +If the provider supports the `status` field/accessor and initialization fails, setting the `status` to `ERROR` indicates the provider is in an error state. If the error is transient in nature (ex: a connectivity failure of some kind) the provider can attempt to resolve this state automatically. + +#### Requirement 2.4.5 + +> The provider **SHOULD** indicate an error if flag resolution is attempted before the provider is ready. + +It's recommended to set an informative `error code`, such as `PROVIDER_NOT_READY` if evaluation in attempted before the provider is initialized. + +see: [error codes](https://openfeature.dev/specification/types#error-code) + +### 2.5. Shutdown + +[![experimental](https://img.shields.io/static/v1?label=Status&message=experimental&color=orange)](https://github.com/open-feature/spec/tree/main/specification#experimental) + +#### Requirement 2.5.1 + +> The provider **MAY** define a `shutdown` function to perform whatever cleanup is necessary for the implementation. +```java +// MyProvider implementation of the dispose function defined in Provider +class MyProvider implements Provider, AutoDisposable { + //... + void dispose() { + // close connections, terminate threads or timers, etc... + } + //... +} +``` + +Providers may maintain remote connections, timers, threads or other constructs that need to be appropriately disposed of. +Provider authors may implement the `shutdown` function to perform relevant clean-up actions. +The precise name of this function is not prescribed by this specification, but should be defined be the SDK. +Relevant language idioms should be considered when choosing the name for this function, in accordance with the resource-disposal semantics of the language in question. \ No newline at end of file diff --git a/specification/sections/03-evaluation-context.md b/specification/sections/03-evaluation-context.md index f484d4e9..6c2929c6 100644 --- a/specification/sections/03-evaluation-context.md +++ b/specification/sections/03-evaluation-context.md @@ -6,7 +6,7 @@ toc_max_heading_level: 4 # 3. Evaluation Context -**Status**: [Experimental](../README.md#document-statuses) +[![experimental](https://img.shields.io/static/v1?label=Status&message=experimental&color=orange)](https://github.com/open-feature/spec/tree/main/specification#experimental) ## Overview @@ -42,7 +42,7 @@ see: [structure](../types.md#structure), [datetime](../types.md#datetime) The key uniquely identifies a field in the `evaluation context` and it should be unique across all types to avoid any collision when marshalling the `evaluation context` by the provider. -### 3.2 Merging Context +### 3.2 Context levels and merging #### Requirement 3.2.1 diff --git a/specification/types.md b/specification/types.md index e3ca38cb..b0ded0a9 100644 --- a/specification/types.md +++ b/specification/types.md @@ -73,8 +73,8 @@ A set of pre-defined reasons is enumerated below: An enumerated error code represented idiomatically in the implementation language. | Error Code | Explanation | -|-----------------------|---------------------------------------------------------------------------------------------| -| PROVIDER_NOT_READY | The value was resolved before the provider was ready. | +| --------------------- | ------------------------------------------------------------------------------------------- | +| PROVIDER_NOT_READY | The value was resolved before the provider was initialized. | | FLAG_NOT_FOUND | The flag could not be found. | | PARSE_ERROR | An error was encountered parsing data, such as a flag configuration. | | TYPE_MISMATCH | The type of the flag value does not match the expected type. | @@ -93,3 +93,13 @@ A structure containing the following fields: A structure which supports definition of arbitrary properties, with keys of type `string`, and values of type `boolean`, `string`, or `number`. This structure is populated by a provider for use by an [Application Author](./glossary.md#application-author) (via the [Evaluation API](./glossary.md#evaluation-api)) or an [Application Integrator](./glossary.md#application-integrator) (via [hooks](./sections/04-hooks.md)). + +### Provider Status + +An enumeration of possible provider states. + +| Status | Explanation | +| --------- | ------------------------------------------------------------------------------- | +| NOT_READY | The provider has not been initialized. | +| READY | The provider has been initialized, and is able to reliably resolve flag values. | +| ERROR | The provider is initialized but is not able to reliably resolve flag values. | diff --git a/tools/specification_parser/specification_parser.py b/tools/specification_parser/specification_parser.py index 27ba8c8e..1cb51174 100644 --- a/tools/specification_parser/specification_parser.py +++ b/tools/specification_parser/specification_parser.py @@ -181,7 +181,7 @@ def content_tree_to_spec(ct): def parse(markdown_file_path): with open(markdown_file_path, "r") as markdown_file: - content_finder = re.compile(r'^(?P#+)(?P[^\n]+)(?P[^#]*)', re.MULTILINE) + content_finder = re.compile(r'^(?P####+)(?P[^\n]+)\n+?.*?\n+?(?P>\s[^#?]*)', re.MULTILINE) parsed = content_finder.findall(markdown_file.read()) return parsed_content_to_heirarchy(parsed) diff --git a/tools/specification_parser/test_specification.md b/tools/specification_parser/test_specification.md index 64eb82d1..e9e12a12 100644 --- a/tools/specification_parser/test_specification.md +++ b/tools/specification_parser/test_specification.md @@ -4,27 +4,27 @@ Some content. -##### Requirement Some requirement +#### Requirement Some requirement > This **MUST** be done. -##### Requirement Some other requirement +#### Requirement Some other requirement > This **MUST NOT** be done. -##### Requirement Another requirement-name +#### Requirement Another requirement-name > This **SHOULD** be done in a certain way. -##### Condition 1 +#### Condition 1 > This is a condition. > -> ##### Condition 1.1 +> #### Condition 1.1 > > > This is a condition. > > -> > ##### Condition 1.1.1 +> > #### Condition 1.1.1 > > > > > This is a condition. > > > @@ -40,7 +40,7 @@ Some content. > > > > > > > This **MAY** be done. > -> ##### Condition 1.2 +> #### Condition 1.2 > > > This is a condition. > > @@ -50,11 +50,11 @@ Some content. Some content. -##### Requirement This is the name of-the-requirement +#### Requirement This is the name of-the-requirement > This **MUST NOT** be done. -##### Condition 2 +#### Condition 2 > This is a condition. >