Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
f0e4ea2
docs: Add background for kv-ir streams.
kirkrodrigues Apr 17, 2025
de2c9fd
docs: Add index for clp-s algorithm steps.
kirkrodrigues Apr 17, 2025
a13e138
docs: Remove extraneous newline.
kirkrodrigues Apr 17, 2025
3a83fa3
docs: Fix grammar errors.
kirkrodrigues Apr 17, 2025
b52d3a7
Add beginning of specification.
kirkrodrigues Apr 17, 2025
c4fb1e2
Refinements.
kirkrodrigues Apr 21, 2025
f197687
Minor edits.
kirkrodrigues Apr 22, 2025
0f23532
Minor edits.
kirkrodrigues Apr 25, 2025
7c562ce
Merge branch 'main' into kv-ir-stream-docs
kirkrodrigues Apr 28, 2025
914b21a
Merge branch 'main' into kv-ir-stream-docs
kirkrodrigues Apr 28, 2025
8e08fea
Revise intro & background; Remove unrevised subsections.
kirkrodrigues May 1, 2025
10be913
Fix linting issues.
kirkrodrigues May 1, 2025
d918e92
Merge branch 'main' into kv-ir-stream-docs
kirkrodrigues May 1, 2025
fd6434b
Remove incomplete specification section.
kirkrodrigues May 2, 2025
768fddf
Improve colours of figures.
kirkrodrigues May 2, 2025
c9955aa
Polish background.
kirkrodrigues May 2, 2025
51331f1
Indicate KV-IR streams section is still under construction.
kirkrodrigues May 2, 2025
b3b9e10
Add subsection about parsing and encoding unstructured text.
kirkrodrigues May 3, 2025
a523319
Add some links to provide context for clp and clp-s.
kirkrodrigues May 3, 2025
15bca78
Replace hash with HTML encoding to avoid confusing with the start of …
kirkrodrigues May 3, 2025
21a728b
Address Copilot review.
kirkrodrigues May 3, 2025
a37f3f7
Minor touch-ups.
kirkrodrigues May 3, 2025
8a02837
Address the rabbit's review.
kirkrodrigues May 3, 2025
4ec532e
Merge branch 'main' into kv-ir-stream-docs
kirkrodrigues May 3, 2025
9b29d97
Reformat paragraph about how clp-s value types are encoded; Fix error…
kirkrodrigues May 3, 2025
af40923
markdownlint fix.
kirkrodrigues May 3, 2025
dd8ff59
Apply suggestions from code review
kirkrodrigues May 4, 2025
bccaa27
Make archive overveiw paragraph more concise.
kirkrodrigues May 4, 2025
77c4ecc
Rewrite paragraph on encoding event schemas.
kirkrodrigues May 4, 2025
14cb42c
Touch-up figures.
kirkrodrigues May 4, 2025
8099992
B to -byte.
kirkrodrigues May 4, 2025
5245a99
Break encoding & storing values into two subsections.
kirkrodrigues May 4, 2025
68bb3bf
Step 5 isn't per event.
kirkrodrigues May 4, 2025
800bd4d
Remove a word.
kirkrodrigues May 4, 2025
44c6d44
Replace type plurals with something that renders better.
kirkrodrigues May 4, 2025
e1c2f47
Make bullet style consistent.
kirkrodrigues May 4, 2025
9a81f9c
Make archive-level schema tree more accurate; Remove name from root n…
kirkrodrigues May 5, 2025
9b90be5
Merge branch 'main' into kv-ir-stream-docs
kirkrodrigues May 5, 2025
d403651
Merge branch 'main' into kv-ir-stream-docs
kirkrodrigues May 5, 2025
f25499d
Order KV-IR streams after project structure.
kirkrodrigues May 5, 2025
04abba2
Fix en dash which should be an em dash.
kirkrodrigues May 6, 2025
781d699
Remove extraneous space.
kirkrodrigues May 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
350 changes: 350 additions & 0 deletions docs/src/dev-guide/design-kv-ir-streams/background.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,350 @@
# Background

Comment on lines +1 to +2

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Clarify document title for context
The main heading # Background is quite generic. Consider renaming it to something more descriptive, for example:

# Background for KV-IR Stream Format

This immediately signals the scope of the document to readers.

To understand the KV-IR stream format, we first need to review the following:

* [How clp-s compresses log events](#clp-s-compression), since the process for KV-IR streams is
similar but makes a different trade-off between resource usage, compression ratio, and search
performance.
* [How clp-s parses and encodes unstructured text values](#parsing--encoding-unstructured-text).
Comment thread
coderabbitai[bot] marked this conversation as resolved.

Comment on lines +5 to +9

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Improve list item parallelism and clarity
The two bullets both start with “How clp-s…”, which feels repetitive. Consider rephrasing to align with a consistent pattern, for example:

  • “clp-s compression of log events”
  • “clp-s parsing and encoding of unstructured text values”

This also reduces the chance of invalid link fragments.

Comment on lines +3 to +9

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify internal link targets and phrasing
The list items reference #clp-s-compression and #parsing--encoding-unstructured-text. Please confirm those anchors match the actual slugified headings (especially the double hyphen for “Parsing & encoding”). You may also simplify phrasing for parallelism, e.g.:

* Clp-S Compression of log events
* Parsing and Encoding Unstructured Text

🏁 Script executed:

#!/bin/bash
# List all markdown headings in the background.md file to verify anchor slugs
grep -nE '^#{1,6} ' docs/src/dev-guide/design-kv-ir-streams/background.md || true

Length of output: 329


Verified internal anchors; phrasing suggestion
I’ve confirmed that the current link targets match the slugified headings in background.md:

  • “clp-s compression” → #clp-s-compression
  • “Parsing & encoding unstructured text” → #parsing--encoding-unstructured-text

For improved parallelism, you may update the list to:

* [Clp-S Compression of log events](#clp-s-compression)
* [Parsing and Encoding Unstructured Text](#parsing--encoding-unstructured-text)

We discuss each below.

## clp-s compression

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Consistent heading casing and terminology
The section title uses sentence case and lowercase “clp-s”. For consistency with other headings and acronyms, use title case and uppercase the acronym, e.g.:

## CLP-S Compression


At a high-level, [clp-s](../../user-guide/core-clp-s.md) compresses log events into what we call
Comment on lines +12 to +14

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Capitalize section heading for consistency
Consider using title case for the “clp-s compression” heading to match others, e.g.:

## CLP-S Compression

archives. Depending on the configured size-threshold for each archive, a set of log events may be
compressed into one or more archives. The archive format is designed so that each archive is
self-contained and independent, allowing archives to be searched concurrently.

Comment on lines +12 to +18

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Use title case and correct hyphenation
For consistency with other headings, rewrite

## clp-s compression

to

## CLP-S Compression

Also change “At a high-level” to “At a high level” to remove the unnecessary hyphen.

To compress a log event into an archive, clp-s needs to do the following:

1. [Compute the event's schema](#computing-a-log-events-schema)
2. [Encode the event's schema](#encoding-log-event-schemas)
3. [Encode the event's values](#encoding-log-event-values)
4. [Store the event's encoded values](#storing-encoded-values)

Finally, when all events for an archive have been processed, clp-s needs to serialize and
[write the archive's data structures](#writing-archives-to-disk). The goal of this process is to
transform the log events into a form that's more compact to store and faster to search.

:::{tip}
To learn more about clp-s, check out the original [research paper][clp-s-paper].
:::

### Computing a log event's schema

A log event's schema is the set of *key* and *value-type* pairs for each KV pair in the log event.
To compute an event's schema, clp-s iterates over every KV pair in the log event to:

* determine the clp-s value-type that should be assigned to the KV pair.
* build a tree representation of the schema---what we call a schema tree.

Consider the example log events in [Figure 1](#figure-1), and their schemas in [Table 1](#table-1)
and [Table 2](#table-2). clp-s' value types, including those used in the schema, are listed in
[Table 3](#table-3).

clp-s assigns a type to a value based on the value's "abstract" type (i.e., whether it's an integer,
float, boolean, string, object, null, or array) and how the value should be encoded. For some
abstract types, clp-s only has one way of encoding it, so it assigns the corresponding clp-s
type---e.g., the integer corresponding to the `timestamp` key. For other abstract types, clp-s can
encode the value in multiple ways, so it assigns the clp-s type where the encoded value will result
in a good trade-off between compactness and efficient searches---e.g., the strings corresponding to
the `level` and `message` keys use different clp-s types.

Once clp-s assigns a type to a value, it can add it to the log event's schema tree. Except for the
root, each node in a schema tree represents a unique key and value-type pair from the schema. For
instance, the tree for the schema in [Table 1](#table-1) is shown in [Figure 2](#figure-2). Since
the tree represents the structure of a structured log event, each internal (non-leaf) node will
always correspond to an `Object` or `StructuredArray`, while the leaf nodes will correspond to
values with primitive types (since `UnstructuredArray` values are encoded as JSON strings, they are
primitives from the perspective of a schema tree). Accordingly, the root node represents the event
object itself, and has no key.

(figure-1)=
:::{card}

```json lines
{
"timestamp": 1744618344394,
"level": "info",
"message": "task_1 completed successfully. 2 task(s) remain.",
"timers": {
"stage_1": 0.753,
"stage_2": null
}
}

{
"timestamp": 1744618344499,
"level": "info",
"message": "task_2 completed successfully. 1 task(s) remain.",
"timers": {
"stage_1": 0.945,
"stage_2": 0.222
}
}
```

+++
**Figure 1**: Two JSON log events.
:::

(table-1)=
:::{card}

| Key | clp-s value-type |
|----------------|------------------|
| timestamp | Integer |
| level | VarString |
| message | ClpString |
| timers | Object |
| timers.stage_1 | Float |
| timers.stage_2 | NullValue |

+++
**Table 1**: The schema for log event #1 in [Figure 1](#figure-1). Nested keys are represented
with dot notation. The value types are described in [Table 3](#table-3).
:::

(table-2)=
:::{card}

| Key | clp-s value-type |
|----------------|------------------|
| timestamp | Integer |
| level | VarString |
| message | ClpString |
| timers | Object |
| timers.stage_1 | Float |
| timers.stage_2 | Float |

+++
**Table 2**: The schema for log event #2 in [Figure 1](#figure-1).
:::

(table-3)=
:::{card}

| clp-s value-type | Description | Node type |
|-------------------|---------------------------------------------------------------------------------------------------------|-----------|
| Integer | A 64-bit integer | Leaf |
| Float | A floating-point number | Leaf |
| Boolean | A boolean | Leaf |
| VarString | A string without whitespace | Leaf |
| DateString | A string representing a timestamp | Leaf |
| ClpString | A string containing whitespace, parsed into an [encoded text AST](#parsing--encoding-unstructured-text) | Leaf |
| NullValue | A null value | Leaf |
| UnstructuredArray | An array that's serialized as a JSON string | Leaf |
| Object | An object | Internal |
| StructuredArray | An array | Internal |

+++
**Table 3**: clp-s value types.
:::

(figure-2)=
::::{card}
:::{mermaid}
%%{
init: {
"theme": "base",
"themeVariables": {
"primaryColor": "#0066cc",
"primaryTextColor": "#fff",
"primaryBorderColor": "transparent",
"lineColor": "#007fff",
"secondaryColor": "#007fff",
"tertiaryColor": "#fff"
}
Comment on lines +148 to +158

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Reduce duplication of Mermaid theme configuration
The init block with themeVariables is identical across diagrams. Consider extracting this into a reusable include or shared snippet (once upstream bug is fixed) to avoid drift and simplify maintenance.

}
Comment on lines +148 to +159

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Reduce duplication of Mermaid theme configuration
The init block with themeVariables is repeated verbatim in multiple diagrams. Consider extracting this snippet into a reusable include or leveraging a shared configuration in conf.py once the upstream bug in sphinxcontrib-mermaid is resolved.

}%%
flowchart LR
rootObj("&lt;Root&gt;: <span style='color: #97ff00'>Object</span>")
messageClpStr("&quot;message&quot;: <span style='color: #97ff00'>ClpString</span>")
levelVarStr("&quot;level&quot;: <span style='color: #97ff00'>VarString</span>")
timersObj("&quot;timers&quot;: <span style='color: #97ff00'>Object</span>")
timersStage1Float("&quot;stage_1&quot;: <span style='color: #97ff00'>Float</span>")
timersStage2Null("&quot;stage_2&quot;: <span style='color: #97ff00'>NullValue</span>")
timestampInt("&quot;timestamp&quot;: <span style='color: #97ff00'>Integer</span>")

rootObj --> timestampInt
rootObj --> levelVarStr
rootObj --> timersObj
timersObj --> timersStage1Float
timersObj --> timersStage2Null
rootObj --> messageClpStr
:::
+++
**Figure 2**: The schema tree for log event &#35;1 in [Figure 1](#figure-1). Each node's label is of
the form `"<key>": <type>`, except for the root which doesn't have an explicit name. Each arrow is
from a parent to a child node.
::::

### Encoding log event schemas

To compactly encode each event's schema in an archive, clp-s represents each schema with a set of
integer IDs corresponding to nodes of an archive-level schema tree. This archive-level schema tree
is built, in part, by merging all events' schema trees and assigning a unique ID to each node. An
event's schema can then be encoded as the IDs of its *leaf* nodes within the tree, since the leaf
nodes are sufficient to rebuild the event's tree by traversing from the leaves to the root. For
instance, [Figure 3](#figure-3) shows the schema tree after adding the example logs
([Figure 1](#figure-1)) to the tree. The events' schema trees have been merged under the
`<Default namespace>` node. Referencing the leaf node IDs, the schema for event &#35;1 can be
encoded as `[3, 4, 6, 7, 9]`, corresponding to the schema's leaf nodes.

As [Figure 3](#figure-3) shows, the archive-level schema tree uses different *namespaces* to store
more than just the KV pairs that appear *in* the event. For instance, the `Metadata` namespace
contains metadata KV pairs like the log event's index in the archive. The `Default` namespace
contains the KV pairs that aren't specific to a special namespace, which in the case of Figure 3,
are the KV pairs that appear in the example log events. As we'll see in future docs, namespaces
also allow clp-s to compress log events that contain namespaces themselves.

To merge an event's schema tree with the archive-level schema tree, clp-s iterates over each pair of
nodes---one from each tree:

* If the nodes have the same key and value-type, and all of their predecessor nodes have matching
key and value-type pairs, clp-s merges the nodes in the resulting tree.
* Otherwise, both nodes are added to the resulting tree, and each is assigned a unique integer ID.

<!-- markdownlint-disable MD013 -->
(figure-3)=
::::{card}
:::{mermaid}
%%{
init: {
"theme": "base",
"themeVariables": {
"primaryColor": "#0066cc",
"primaryTextColor": "#fff",
"primaryBorderColor": "transparent",
"lineColor": "#007fff",
"secondaryColor": "#007fff",
"tertiaryColor": "#fff"
}
}
}%%
flowchart LR
root("<span style='color: #ffbe00'>-1</span> &lt;Root&gt;")
metadataNamespaceRoot("<span style='color: #ffbe00'>0</span> &lt;Metadata namespace&gt;: <span style='color: #97ff00'>Metadata</span>")
logEventIdxInt("<span style='color: #ffbe00'>1</span> &quot;log_event_idx&quot;: <span style='color: #97ff00'>Integer</span>")
defaultNamespaceRootObj("<span style='color: #ffbe00'>2</span> &lt;Default namespace&gt;: <span style='color: #97ff00'>Object</span>")
messageClpStr("<span style='color: #ffbe00'>3</span> &quot;message&quot;: <span style='color: #97ff00'>ClpString</span>")
levelVarStr("<span style='color: #ffbe00'>4</span> &quot;level&quot;: <span style='color: #97ff00'>VarString</span>")
timersObj("<span style='color: #ffbe00'>5</span> &quot;timers&quot;: <span style='color: #97ff00'>Object</span>")
timersStage1Float("<span style='color: #ffbe00'>6</span> &quot;stage_1&quot;: <span style='color: #97ff00'>Float</span>")
timersStage2Null("<span style='color: #ffbe00'>7</span> &quot;stage_2&quot;: <span style='color: #97ff00'>NullValue</span>")
timersStage2Float("<span style='color: #ffbe00'>8</span> &quot;stage_2&quot;: <span style='color: #97ff00'>Float</span>")
timestampInt("<span style='color: #ffbe00'>9</span> &quot;timestamp&quot;: <span style='color: #97ff00'>Integer</span>")

root --> metadataNamespaceRoot
metadataNamespaceRoot --> logEventIdxInt
root --> defaultNamespaceRootObj
defaultNamespaceRootObj --> timestampInt
defaultNamespaceRootObj --> levelVarStr
defaultNamespaceRootObj --> timersObj
timersObj --> timersStage1Float
timersObj --> timersStage2Null
timersObj --> timersStage2Float
defaultNamespaceRootObj --> messageClpStr
:::
+++
**Figure 3**: The archive's schema tree after adding the log events from [Figure 1](#figure-1). Each
node's label is of the form `<ID> <key>: <type>` except for the namespace nodes which don't have an
explicit name, and the root which has neither an explicit name nor type.
::::
<!-- markdownlint-enable MD013 -->

### Encoding log event values

For each log event, clp-s encodes each value using an encoding method for the value's specific type.
The goal of each method is to deduplicate any repetitive information (e.g., deduplicating repeated
`VarString` values with a dictionary) and then represent the value with a 64-bit integer.
[Table 4](#table-4) lists how clp-s encodes each value type. Most value types are encoded
conventionally with the following exceptions:
Comment on lines +259 to +263

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Wrap long lines for readability
Several sentences and table entries exceed 80 characters (MD013). Please reflow paragraphs and break long lines (including table rows) at approximately 80 characters to improve readability.

🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

259-259: Line length
Expected: 80; Actual: 100

(MD013, line-length)


260-260: Line length
Expected: 80; Actual: 98

(MD013, line-length)


261-261: Line length
Expected: 80; Actual: 89

(MD013, line-length)


262-262: Line length
Expected: 80; Actual: 89

(MD013, line-length)


262-262: Link fragments should be valid
null

(MD051, link-fragments)


* For the values encoded as dictionary IDs, clp-s simply stores the value in a dictionary and maps
it to a unique integer ID.
* For `ClpString` values, clp-s encodes each component separately.
* For `NullValue` values, clp-s doesn't need to encode anything since they don't need to be stored
explicitly---a `NullValue` leaf node already indicates that the corresponding column of the ERT is
null.

(table-4)=
:::{card}

| clp-s value-type | Encoding |
|-----------------------------|-------------------------------------------------------------|
| Integer | 8-byte integer |
| Float | 8-byte IEEE-754 double-precision float |
| Boolean | 1-byte integer |
| VarString | 8-byte dictionary ID |
| DateString | 8-byte epoch timestamp & 8-byte format string dictionary ID |
| ClpString | *See below* |
| --> Format string | 8-byte dictionary ID |
| --> Encoded variable values | Collection of 8-byte integers |
| --> String variable values | Collection of 8-byte dictionary IDs |
| UnstructuredArray | Same as ClpString |
| NullValue | N/A |

+++
**Table 4**: How clp-s encodes each of its leaf node value types.
:::

clp-s' two array types are used to encode arrays with different characteristics. `StructuredArray`
values are similar to `Object` values in that all of their elements will be added to the schema
tree. Accordingly, this type is more appropriate for encoding arrays whose elements don't change
types significantly between log events; otherwise, the schema tree would be significantly larger.
For other arrays, the `UnstructuredArray` type is more appropriate---since it's encoded as a JSON
string, its elements won't be added to the tree. Nonetheless, values within these arrays can still
be searched.

### Storing encoded values

clp-s stores a log event's encoded values in a table corresponding to its schema, with one column
for each node in the schema. We refer to this table as an encoded record table (ERT). By grouping
events with the same schema into an ERT, clp-s avoids redundantly storing the schema per event
(unlike, for example, JSON). In addition, ERTs are efficient to search since all columns store
integers.

### Writing archives to disk

To write an archive's data structures to disk, clp-s serializes them and writes them to one or more
general-purpose compression streams. Applying general-purpose compression allows us to mitigate some
of the inefficient encodings (e.g., encoding `Boolean` values as integers) used to maintain
efficient search performance. For some data structures, like dictionaries, clp-s writes them to disk
as they are built; yet for other data structures, like the ERTs, clp-s buffers them in memory until
the archive is complete.

## Parsing & encoding unstructured text

clp-s uses [clp](../../user-guide/core-unstructured/clp.md)'s algorithm to parse and encode
Comment on lines +318 to +320

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Refine heading style and symbols
The heading “## Parsing & encoding unstructured text” mixes the ampersand and sentence case. For uniformity, spell out “and” and use title case, e.g.:

## Parsing and Encoding Unstructured Text
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

320-320: Line length
Expected: 80; Actual: 91

(MD013, line-length)

unstructured text. Unstructured text is a string that contains zero or more variable values
interspersed with non-variable (static) text. For example, in [Figure 1](#figure-1), log event
&#35;1's `message` value is unstructured text containing the variable values `task_1` and `2`. At a
high-level, clp's algorithm uses a set of user-defined regular expressions to match each variable
value in the unstructured text, decomposing the text into:

* a format string---i.e., the unstructured text with variable values replaced with placeholders.
* string variable values.
* encoded variable values---i.e., variable values which have been encoded as 64-bit integers.

Collectively, we refer to these three components as an *encoded text AST*. For instance, log event
&#35;1's `message` value would be decomposed into the following encoded text AST:

* Format string: `\x12 completed successfully. \x11 task(s) remain.`
* `\x12` and `\x11` are variable placeholders representing string and integer variables,
respectively.
* String variable values: `["task_1"]`
* Encoded variable values: `[1]`

:::{note}
The clp codebase refers to an encoded text AST's string variable values as "dictionary variables,"
since they're typically stored in a dictionary. This may change as we update the codebase.
:::

:::{tip}
To learn more about clp, check out the original [research paper][clp-paper].
:::

[clp-paper]: https://www.usenix.org/system/files/osdi21-rodrigues.pdf
[clp-s-paper]: https://www.usenix.org/system/files/osdi24-wang-rui.pdf
29 changes: 29 additions & 0 deletions docs/src/dev-guide/design-kv-ir-streams/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# KV-IR streams

The key-value pair internal representation (abbreviated as KV-IR) stream format is a storage format
for dynamically structured (e.g., JSON) logs. Compared to a JSON file, the KV-IR stream format is
typically smaller and faster for clp-s to compress. Compared to clp-s' archive format, the KV-IR
stream format buffers less data in-memory, making it more suitable for use in resource-constrained
environments and low-latency use cases (e.g., logging libraries). This section describes the format
as well as the key design decisions behind it.

::::{grid} 1 1 1 1
:gutter: 2

:::{grid-item-card}
:link: background
Background
^^^
Any necessary information to understand the stream's format.
:::
::::

:::{warning}
🚧 This section is still under construction.
:::

:::{toctree}
:hidden:

background
:::
Comment on lines +25 to +29

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Add blank line before toctree for MyST
Ensure there’s an empty line preceding the :::{toctree} directive so MyST parses it cleanly and avoids rendering glitches.

1 change: 1 addition & 0 deletions docs/src/dev-guide/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,5 +80,6 @@ tooling-gh-workflows
:hidden:

design-project-structure
design-kv-ir-streams/index
design-parsing-wildcard-queries
:::
4 changes: 2 additions & 2 deletions docs/src/dev-guide/tooling-gh-workflows.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ shown below.
"primaryColor": "#0066cc",
"primaryTextColor": "#fff",
"primaryBorderColor": "transparent",
"lineColor": "#9580ff",
"secondaryColor": "#9580ff",
"lineColor": "#007fff",
"secondaryColor": "#007fff",
Comment on lines +24 to +25

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Verify colour contrast and consistency
The lineColor and secondaryColor values have been updated to #007fff. Please confirm these colours maintain sufficient contrast per WCAG guidelines and remain consistent with the project’s branding across all Mermaid diagrams.

"tertiaryColor": "#fff"
}
}
Expand Down