Skip to content

Seal RelationType as a sum-type with one parse boundary (links-va-001-relation-type-raq.1)#206

Merged
brandon-fryslie merged 1 commit into
masterfrom
va-001-relation-type
Jun 10, 2026
Merged

Seal RelationType as a sum-type with one parse boundary (links-va-001-relation-type-raq.1)#206
brandon-fryslie merged 1 commit into
masterfrom
va-001-relation-type

Conversation

@brandon-fryslie

Copy link
Copy Markdown
Collaborator

Summary

Implements F-11 from the va-001 epic (links-va-001-relation-type-raq): Relation.Type was a raw string, so every layer carried its own string-equality defenses. model.RelationType is now the sealed sum (RelBlocks / RelParentChild / RelRelatedTo) and ParseRelationType is the single string→type gate at trust boundaries ([LAW:types-are-the-program], [LAW:single-enforcer]).

  • ParseRelationType — called at the CLI --type flags and in ImportRelation; preserves the exact legacy error message. Store-side duplicate validation chains are deleted, not kept defensively.
  • StoreEndpoints — the blocks orientation rule (store encodes dependent→dependency, reverse of human order) lives in one involution; depStoreEndpoints and depRelationForCLI's early-return branch are gone — the flip now applies unconditionally with variability in the value ([LAW:dataflow-not-control-flow]).
  • CanonicalEndpoints — the related-to undirected-sort normalization, previously 3 inline copies (AddRelation / RemoveRelation / ImportRelation), lives in one method ([LAW:one-source-of-truth]).
  • ListRelationsForIssue takes a variadic type-set filter instead of a ""-sentinel string.
  • merge/shapemap updated; the lifeboat salvage path uses a raw conversion deliberately (conserves bytes; Doctor re-checks) and is marked as the law exception.

Drift from the epic (design of record, sites re-located by grep)

Behavior changes (deliberate, [LAW:no-silent-failure])

  • dep rm --type <junk> now fails at parse (exit 3, documented message) instead of a NotFound for a nonsense key.
  • dep ls --type <junk> now fails at parse instead of silently printing nothing.

Verification

  • go test ./... clean; new pins: TestParseRelationType, TestRelationTypeStoreEndpoints (involution), TestRelationTypeCanonicalEndpoints, TestDepRejectsUnknownRelationType (CLI boundary, all three subcommands).
  • Old-vs-new binary byte-identical output on the live store (dep ls text/JSON/filters, show, backlog).
  • Scratch-workspace round trips: blocks orientation, related-to reverse-order rm (canonicalization), self-loop rejection, parse error exit codes.

…oundary (links-va-001-relation-type-raq.1)

Relation.Type was a raw string, forcing eight scattered string-equality
defenses across CLI and store. model.RelationType is now the sealed set
(RelBlocks/RelParentChild/RelRelatedTo); ParseRelationType is the single
string->type gate at trust boundaries (CLI --type flags, import payloads).

- StoreEndpoints absorbs the blocks orientation rule (one involution,
  serves both store and display direction); CanonicalEndpoints absorbs
  the related-to undirected-sort normalization (was 3 inline copies).
- Store-side duplicate validation chains deleted; AddRelationInput and
  RemoveRelation take the typed value.
- ListRelationsForIssue filter is a variadic type set, not a ""-sentinel.
- Behavior improvement: dep rm/ls with an unknown --type now fail loudly
  at parse (exit 3) instead of NotFound / silently matching nothing.
- Epic drift of record: legacydolt sites no longer exist; the
  GetIssueDetail switch was already absorbed into bucketRelations.

Verified: go test ./... clean; old-vs-new binary byte-identical on live
store reads; blocks/related-to round trips + error paths exercised on a
scratch workspace.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Z.ai Coding Agent Review

This is a clean refactor that introduces RelationType as a sealed sum type with ParseRelationType as the single parse boundary, and moves orientation/normalization logic onto the type as StoreEndpoints/CanonicalEndpoints. It eliminates three independent string-validation chains (CLI, AddRelation, ImportRelation) in favor of one gate, and replaces the sentinel-string ListRelationsForIssue signature with a variadic ...RelationType — all consistent with [LAW:types-are-the-program], [LAW:single-enforcer], and [LAW:one-source-of-truth].

No must-change items found. Two pre-existing / low-priority observations for the author's attention:

  1. depRelationLine default case (LINE 229) — Now unreachable dead code; the sealed type guarantees only the three named constants flow through, so the default branch can never fire. Consider removing it so the compiler catches any future gap instead of silently falling through.

  2. Inconsistent string() conversion at SQL boundaryImportRelation (LINE 344) and RemoveRelation (LINE 303) explicitly cast to string() before passing to ExecContext, but AddRelation (LINE 270) passes rel.Type as the raw RelationType. If the driver handles both (likely, since the test suite presumably passes), the explicit casts in the other two call sites are unnecessary; if the casts are needed, the AddRelation path has a latent bug. One pattern or the other should be applied consistently.

✅ Approved

@brandon-fryslie brandon-fryslie merged commit 20ff512 into master Jun 10, 2026
6 checks passed
@brandon-fryslie brandon-fryslie deleted the va-001-relation-type branch June 10, 2026 08:44
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.

1 participant