same-as

August 4, 2026 · View on GitHub

Asserts that two or more blocks hold the same value. Where affects only checks that linked blocks were co-edited, same-as compares their contents — catching duplicated constants, lists, and versions that silently drift apart.

Unlike affects, it also runs on a full-tree scan, not only on a diff.

Syntax

AttributeValueDefault
same-asfile:name, or :name for the same file; comma-separated for multiple
same-as-patternregex; the (?P<value>…) group, or the whole matchwhole line
same-as-modeset, sequence, single, subsetset
same-as-formatnumerictext

Example

With no extra attributes the whole trimmed content is compared as text, so both blocks must be identical. This fits content you cannot factor into a shared symbol — here, a command documented in two places:

README.md:

[//]: # (<block same-as="docs/ci.md:pre-commit">)

    git diff --patch --cached | blockwatch

[//]: # (</block>)

docs/ci.md:

[//]: # (<block name="pre-commit">)

    git diff --patch --cached | blockwatch

[//]: # (</block>)

Most couplings, though, do not share verbatim text. For those, each block describes how to read itself.

Extract values with same-as-pattern

Each side extracts one token per line via its own regex — the (?P<value>…) capture group, or the whole match if there is none. Lines that do not match are skipped. Because each block self-describes, blocks in different formats can still be compared:

// <block same-as="README.md:supported-env-vars" same-as-pattern="BLOCKWATCH_AI_[A-Z_]+">
const API_KEY: &str = "BLOCKWATCH_AI_API_KEY";
const API_URL: &str = "BLOCKWATCH_AI_API_URL";
// </block>
[//]: # (<block name="supported-env-vars" same-as-pattern="BLOCKWATCH_AI_[A-Z_]+">)

- `BLOCKWATCH_AI_API_KEY`: API key.
- `BLOCKWATCH_AI_API_URL`: API URL.

[//]: # (</block>)

Comparison modes

same-as-modeMeaning
set (default)Order- and duplicate-insensitive; the two token sets must be equal
sequenceOrder-sensitive list equality
singleExactly one token per side — "there is exactly one version"
subsetDirectional: every token here must also appear in the target

subset is the one directional mode. It fits cases where one side is a legitimate subset of the other — a test fixture exercising only some of the declared environment variables, say:

// <block same-as="src/config.rs:env-vars" same-as-mode="subset" same-as-pattern="BLOCKWATCH_AI_[A-Z_]+">
const API_KEY: &str = "BLOCKWATCH_AI_API_KEY";
// </block>

Numeric comparison

same-as-format="numeric" parses each token as a number before comparing, so the same quantity written in different numeric forms still agrees.

A timeout shared between a Rust backend and a TypeScript frontend is a good case: the value cannot be imported across the language boundary, and the two sides spell it differently. Wrapping the tag inline around just the literal keeps the block content down to the number itself, so no same-as-pattern is needed:

src/backend.rs:

const TIMEOUT: Duration = Duration::from_secs_f64(/* <block same-as="app/config.ts:timeout" same-as-format="numeric"> */ 60.0 /* </block> */);

app/config.ts:

export const timeout = /* <block name="timeout"> */ 60 /* </block> */; // seconds

Rust's from_secs_f64 takes a float (60.0) while TypeScript uses a plain 60; numeric parses both and they compare equal. Under text comparison, "60.0" != "60" would fail.

Notes

  • Which block governs what. The source block's same-as-mode and same-as-format govern the comparison. Each block's own same-as-pattern governs only how that block is read.
  • Violations: a missing target block, a non-numeric token under numeric, or a single / subset side with the wrong number of tokens.
  • Hard errors (not violations): an unrecognized same-as-mode or same-as-format value, or an invalid regex.
  • Because same-as fires without a diff, a periodic full-tree blockwatch run catches drift that a diff-only check would miss. See CI integration.
  • Targets are read, not reported. Under a diff, a target the diff did not touch is still resolved and compared, but it does not appear in a --verbosity run report. See Reports Under a Diff.

Validators · README