docs(6206): rulebook model comments state the design, not the history of the rulebook

The relation kinds say what each prevents without the rule-46 split story;
the removed tier, subscriptions and suppressions are stated as what the
model does not have, with the migration that dropped them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-21 20:38:01 -04:00
co-authored by Claude Opus 5.5
parent a01f5492bc
commit cad74df190
+23 -30
View File
@@ -92,15 +92,13 @@ class Rule(Base, TimestampMixin, SoftDeleteMixin):
# WHEN this rule applies — the trigger, not the instruction. Required of # WHEN this rule applies — the trigger, not the instruction. Required of
# new rules at the service layer and nullable here, because rules written # new rules at the service layer and nullable here, because rules written
# before migration 0088 have none and a migration cannot invent one. # before migration 0088 have none and a migration cannot invent one.
# It carries three jobs at once (note 3026): it is the readable form of # It carries three jobs at once: it is the readable form of the canon tag,
# the canon tag, the half of the document that makes a rule findable by # the half of the document that makes a rule findable by meaning, and —
# meaning, and — since milestone 394 removed the always-on tier — the ONLY # with no always-on tier — the ONLY thing that decides whether a rule ever reaches a session at all. A rule
# thing that decides whether a rule ever reaches a session at all. A rule
# with no trigger is not a quiet rule, it is an unreachable one. # with no trigger is not a quiet rule, it is an unreachable one.
when_to_apply: Mapped[str | None] = mapped_column(Text, nullable=True) when_to_apply: Mapped[str | None] = mapped_column(Text, nullable=True)
# WHAT KIND of instruction this is. `tier` used to sit beside this and # WHAT KIND of instruction this is — the only axis on a rule; delivery
# carry delivery; milestone 394 removed it, so kind is now the only axis # belongs entirely to retrieval.
# on a rule and delivery belongs entirely to retrieval.
# `rule` must be FOLLOWED: ignoring it breaks something or crosses a # `rule` must be FOLLOWED: ignoring it breaks something or crosses a
# boundary. `preference` is how this person wants work DONE: ignoring it # boundary. `preference` is how this person wants work DONE: ignoring it
# costs consistency, not correctness. # costs consistency, not correctness.
@@ -116,7 +114,7 @@ class Rule(Base, TimestampMixin, SoftDeleteMixin):
# trigger-dominated document, ownership-scoped search, the retrieval arms, # trigger-dominated document, ownership-scoped search, the retrieval arms,
# relations, and `rule_versions`, which is where its drift is recorded. # relations, and `rule_versions`, which is where its drift is recorded.
# Defaults to `rule` so nothing changes force on upgrade. # Defaults to `rule` so nothing changes force on upgrade.
# CHECK ck_rules_kind (migration 0098, rule 36). # CHECK ck_rules_kind (migration 0098).
kind: Mapped[str] = mapped_column(Text, default="rule", server_default="rule") kind: Mapped[str] = mapped_column(Text, default="rule", server_default="rule")
why: Mapped[str | None] = mapped_column(Text, nullable=True) why: Mapped[str | None] = mapped_column(Text, nullable=True)
how_to_apply: Mapped[str | None] = mapped_column(Text, nullable=True) how_to_apply: Mapped[str | None] = mapped_column(Text, nullable=True)
@@ -141,7 +139,7 @@ class Rule(Base, TimestampMixin, SoftDeleteMixin):
DateTime(timezone=True), nullable=True DateTime(timezone=True), nullable=True
) )
# The record that caused this rule — the edge notes and tasks already # The record that caused this rule — the edge notes and tasks already
# have. Rule 46's `why` names note 2813 in prose; this is that link as a # have. A `why` can name its origin in prose; this is that link as a
# field, so it survives a rewording of the paragraph. # field, so it survives a rewording of the paragraph.
arose_from_id: Mapped[int | None] = mapped_column( arose_from_id: Mapped[int | None] = mapped_column(
BigInteger, ForeignKey("notes.id", ondelete="SET NULL"), nullable=True BigInteger, ForeignKey("notes.id", ondelete="SET NULL"), nullable=True
@@ -175,10 +173,9 @@ class Rule(Base, TimestampMixin, SoftDeleteMixin):
} }
# Which global AREA a rule is about (milestone 307). Points at the canonical # Which global AREA a rule is about. Points at the canonical catalog, NEVER at
# catalog, NEVER at a project's `systems` row: a rule that spans projects # a project's `systems` row: a rule that spans projects cannot be chained to
# cannot be chained to one project's vocabulary. This is the edge three # one project's vocabulary.
# projects were drawing by hand, as rule text copied into a System's charter.
rule_systems = Table( rule_systems = Table(
"rule_systems", "rule_systems",
Base.metadata, Base.metadata,
@@ -188,7 +185,7 @@ rule_systems = Table(
) )
# Which MOMENTS of work a rule arrives at (milestone 458). A rule mounted on a # Which MOMENTS of work a rule arrives at. A rule mounted on a
# moment is delivered whenever that moment happens, by lookup and with no # moment is delivered whenever that moment happens, by lookup and with no
# score — the route for a rule that is about WHEN something is done rather # score — the route for a rule that is about WHEN something is done rather
# than what it is about, which semantic retrieval structurally misses. The # than what it is about, which semantic retrieval structurally misses. The
@@ -204,17 +201,15 @@ rule_moments = Table(
) )
class RuleRelation(Base, CreatedAtMixin): class RuleRelation(Base, CreatedAtMixin):
"""A typed edge between two rules. Each kind exists because its ABSENCE """A typed edge between two rules. Each kind exists because without it a
forced a workaround somewhere in the operator's rulebook (note 3026). rulebook needs a workaround.
- ``co_surfaces`` — these fail together, so they must arrive together. - ``co_surfaces`` — these fail together, so they must arrive together.
Without it, the only way to guarantee that was to merge them into one Without it, the only way to guarantee that is to merge them into one
row, which is what happened to rule 46: split into 144, folded back the row, because either rule could surface without the other.
same day because "either rule could surface without the other."
Symmetric in meaning; stored once and read both ways. Symmetric in meaning; stored once and read both ways.
- ``overrides`` — this rule supersedes that one for its scope. Only - ``overrides`` — this rule supersedes that one for its scope. Without
*suppression* existed, so an override had to be written as a parallel it, an override is a parallel rule that drifts from its parent.
rule that then drifts from its parent.
- ``elaborates`` — this rule adds local specifics to that one; surfacing - ``elaborates`` — this rule adds local specifics to that one; surfacing
the parent brings the addendum with it. the parent brings the addendum with it.
@@ -232,7 +227,7 @@ class RuleRelation(Base, CreatedAtMixin):
to_rule_id: Mapped[int] = mapped_column( to_rule_id: Mapped[int] = mapped_column(
BigInteger, ForeignKey("rules.id", ondelete="CASCADE"), index=True BigInteger, ForeignKey("rules.id", ondelete="CASCADE"), index=True
) )
# CHECK ck_rule_relations_kind (migration 0088, rule 36). # CHECK ck_rule_relations_kind (migration 0088).
kind: Mapped[str] = mapped_column(Text) kind: Mapped[str] = mapped_column(Text)
note: Mapped[str | None] = mapped_column(Text, nullable=True) note: Mapped[str | None] = mapped_column(Text, nullable=True)
@@ -251,10 +246,8 @@ class RuleRelation(Base, CreatedAtMixin):
} }
# `project_rulebook_subscriptions`, `project_rule_suppressions` and # A rule's home is its scope: a rule in a rulebook topic is global, a rule on
# `project_topic_suppressions` lived here until milestone 414 (migration 0101). # a project applies to that project, and retrieval reads that directly. There
# A rule's home is its scope now: a rule in a rulebook topic is global, a rule # are no per-project subscriptions or suppressions (migration 0101 dropped
# on a project applies to that project, and retrieval reads that directly. A # those tables); a project that departs from a global rule writes a project
# subscription had stopped changing anything a session received, and a # rule with an `overrides` relation.
# suppression muted rules from a subscription. A project that departs from a
# global rule writes a project rule with an `overrides` relation instead.