Skip to content

fix: bracketed link labels drop the edge; add released_in (#48, #49) - #50

Merged
RichardHightower merged 3 commits into
mainfrom
fix/link-re-bracketed-labels
Aug 10, 2026
Merged

fix: bracketed link labels drop the edge; add released_in (#48, #49)#50
RichardHightower merged 3 commits into
mainfrom
fix/link-re-bracketed-labels

Conversation

@RichardHightower

Copy link
Copy Markdown
Contributor

Two independent fixes, one commit each, plus the release bump.

Commit Issue Change
fix: match link labels containing brackets #48 LINK_RE accepts balanced brackets. One regex, one call site.
feat: add released_in to KNOWN_RELS #49 One frozenset entry plus the three prose copies.
chore: release 0.4.0 both Four manifests, README, CHANGELOG.

Why #48 is worth reading first

LINK_RE's label class was [^\]]+, which stops at the first ]. So a
bracketed label matched nothing:

>>> LINK_RE.findall('- [[AREA NAME]](/requirements/area-name.md)')
[]

The important part is what that does downstream. It produces a missing edge,
not a broken one — and validate reports broken link only for edges that
exist. The orphan check needs a concept to have neither inbound nor
outbound links, so any concept carrying one outbound link loses its catalog
backlink with zero diagnostics. I built that bundle to check: validate
returned 0 issues and backlinks returned [].

The concept itself is never lost — load_bundle globs every file, so it stays
queryable by path. Only the inbound edge goes.

This matters because the capture plugins interpolate concept titles into catalog
links unescaped, and bracketed titles are ordinary in exported wiki content
([AREA] prefixes, [DEPRECATED] suffixes). Companion issues cover that
producer side; this is the reader half, and it is the half that also repairs
bundles already on disk.

The regex, and two ways I got it wrong first

LINK_RE = re.compile(r"\[((?:\[[^\[\]]*\]|[^\]])+)\]\(([^)]+)\)")

The label alternation keeps [^\]] — the entirety of the old language — and
adds balanced pairs, tried first. Keeping the old branch is what makes it a
superset. Two earlier drafts regressed:

  • An escape-aware branch (\\.) swallowed the closing bracket on a label ending
    in a backslash, so [a\](/c.md) stopped matching.
  • * instead of + started matching [](/x.md), an empty label the old pattern
    rejects — which would have added edges to existing bundles.

Measured, rather than asserted:

Check Result
20k generated links (trailing \, embedded ], multi-bracket labels) 0 lost
sample-okf edge sets, old vs new identical — 0 lost, 0 gained
[](/x.md) still unmatched, as before
"[" * 200 < 1 ms, no backtracking blowup

test_link_re_is_a_superset_of_the_plain_form pins both regressions so they
cannot come back.

#49

released_in always worked — the guard passes unknown non-empty rels through
unchanged. Only the vocabulary was missing, so a bundle modelling releases got
one info line per shipped work item.

Worth noting this repo already has a release axis on the worklog side:
milestone is a core work-item field and bin/ia_graph.py turns it into
edge(key, "targets", "release/" + milestone). Whether the two should be
unified under one name is a design question I have not assumed an answer to.

The rel list is duplicated in three prose files, so those move together and the
new test pins them to the frozenset.

Verification

python3 tests/test_okf_graph.py          # 29/29
bash tests/test_okf_curate.sh            # ok
python3 scripts/okf-graph.py validate sample-okf --strict   # 0 errors, 0 issues

Both new tests fail on the old regex (27/28) and pass on the new one.

Closes #48
Closes #49

RichardHightower and others added 3 commits August 10, 2026 11:26
LINK_RE used [^]]+ for the label, which stops at the first ], so a bracketed
label matched nothing:

    [[AREA NAME]](/requirements/area-name.md)  ->  no match

That produces a MISSING edge, not a broken one, and validate only reports
broken edges. The orphan check needs a concept to have neither inbound nor
outbound links, so anything carrying one outbound link silently lost its
catalog backlink with zero diagnostics. Bracketed titles are routine in
exported wiki content and the capture plugins interpolate titles unescaped.

The new label alternation keeps [^]] -- the entirety of the old language --
and adds balanced [...] pairs, tried first. Keeping the old branch is what
makes this a superset. Two earlier attempts regressed: an escape-aware branch
swallowed labels ending in a backslash ([a\](/c.md)), and * instead of +
started matching the empty label.

Measured: 0 losses over 20k generated links covering trailing backslashes,
embedded ] and multi-bracket labels; identical edge sets on sample-okf; empty
label still unmatched; "[" * 200 returns in under a millisecond.

Tests fail on the old regex (27/28) and pass on the new one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011XXDjM9WnCtsdRTU441WUu
A bundle that models releases emitted one info line per edge:

    info: non-standard rel 'released_in' (allowed but uncommon)

That is one per shipped work item. The relation already worked -- the guard at
extract_frontmatter_links passes unknown non-empty rels through unchanged -- so
only the vocabulary was missing. The cost was noise, and noise is what makes
piping validate through a severity filter a habit.

This repo already models a release axis on the worklog side: milestone is a
core work-item field (bin/item_fields.py) and bin/ia_graph.py turns it into
edge(key, "targets", "release/" + milestone). So "which release did this ship
in" is already first-class here; bundles just had no way to say it. Whether
the two should be unified under one name is a design question I have not
assumed an answer to.

The rel list is duplicated in three prose files, so typed-edges.md, SKILL.md
and graph-engineer.md are updated together and the new test pins them to the
frozenset so they cannot drift apart.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011XXDjM9WnCtsdRTU441WUu
Bumps the four manifests the consistency test guards, plus the README version
row and a release bullet. .grok-plugin/marketplace.json carries the version
twice -- once at the top level, once in the plugin entry -- and the test only
checks the entry, so the top-level one is easy to leave stale.

29/29 tests pass; validate sample-okf --strict is clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011XXDjM9WnCtsdRTU441WUu
@RichardHightower
RichardHightower merged commit e4b71ca into main Aug 10, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add released_in to KNOWN_RELS Bracketed link labels are dropped by LINK_RE, and validate does not report the missing edge

1 participant