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
| Attribute | Value | Default |
|---|---|---|
same-as | file:name, or :name for the same file; comma-separated for multiple | — |
same-as-pattern | regex; the (?P<value>…) group, or the whole match | whole line |
same-as-mode | set, sequence, single, subset | set |
same-as-format | numeric | text |
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-mode | Meaning |
|---|---|
set (default) | Order- and duplicate-insensitive; the two token sets must be equal |
sequence | Order-sensitive list equality |
single | Exactly one token per side — "there is exactly one version" |
subset | Directional: 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-modeandsame-as-formatgovern the comparison. Each block's ownsame-as-patterngoverns only how that block is read. - Violations: a missing target block, a non-numeric token under
numeric, or asingle/subsetside with the wrong number of tokens. - Hard errors (not violations): an unrecognized
same-as-modeorsame-as-formatvalue, or an invalid regex. - Because
same-asfires without a diff, a periodic full-treeblockwatchrun 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
--verbosityrun report. See Reports Under a Diff.
← Validators · README