Add if.dave.v1.6 style support

This commit is contained in:
danny 2025-12-27 07:00:17 +00:00
parent 6431482378
commit 8a6a83e306
3 changed files with 285 additions and 58 deletions

View file

@ -27,6 +27,7 @@ def generate_shadow_dossier(*, style_id: str, source_text: str, source_path: str
"if.dave.v1.1",
"if.dave.v1.2",
"if.dave.v1.3",
"if.dave.v1.6",
"if.dave.fr.v1.2",
"if.dave.fr.v1.3",
"dave",
@ -34,11 +35,19 @@ def generate_shadow_dossier(*, style_id: str, source_text: str, source_path: str
"if://bible/dave/v1.1",
"if://bible/dave/v1.2",
"if://bible/dave/v1.3",
"if://bible/dave/v1.6",
"if://bible/dave/fr/v1.2",
"if://bible/dave/fr/v1.3",
}:
style = style_id.lower()
locale = "fr" if style in {"if.dave.fr.v1.2", "if.dave.fr.v1.3", "if://bible/dave/fr/v1.2", "if://bible/dave/fr/v1.3"} else "en"
if style in {"if.dave.v1.6", "if://bible/dave/v1.6"}:
return _generate_dave_v1_6_mirror(
source_text=source_text,
source_path=source_path,
action_pack=action_pack,
locale=locale,
)
if style in {"if.dave.v1.3", "if.dave.fr.v1.3", "if://bible/dave/v1.3", "if://bible/dave/fr/v1.3"}:
return _generate_dave_v1_3_mirror(
source_text=source_text,
@ -64,6 +73,7 @@ class _SourceSection:
class _RenderContext:
seed: str
locale: str = "en"
voice: str = "v1.3"
used_callouts: set[str] = field(default_factory=set)
used_diagrams: set[str] = field(default_factory=set)
used_paragraphs: set[str] = field(default_factory=set)
@ -138,6 +148,21 @@ _DAVE_REFRAME_TAILS = [
"so we can keep this on the roadmap without letting it live in production forever",
]
_DAVE_REFRAME_TAILS_V16 = [
"so we can route it to governance and avoid irreversible commitments",
"so we can defer the decision behind a review cycle with an owner and an expiry date",
"so we can treat this as an artifact until the audit window closes",
"so we can keep the narrative intact while the enforcement stays optional",
"so we can record it as progress without changing production behavior",
"so we can time-box the exception and pretend it was a strategy",
"so we can preserve plausible deniability while still blocking something measurable",
"so we can turn it into a stop condition instead of a slide title",
"so we can align on who owns the risk, not who owns the deck",
"so we can keep it off the incident timeline and on the roadmap",
"so we can make drift visible without making accountability mandatory",
"so we can keep the decision pending until it becomes irrelevant",
]
_DAVE_REFRAME_TAILS_FR = [
"afin que nous puissions aligner un responsable, un contrôle en amont et une date dexpiration",
"afin que nous puissions le socialiser avec les parties prenantes et figer un critère de blocage",
@ -270,7 +295,10 @@ def _daveify_callout_reframe(callout: str, *, ctx: _RenderContext, key: str) ->
continue
if re.match(r"^>\s*\*\*[^*]+\*\*:", stripped):
continue
tails = _DAVE_REFRAME_TAILS_FR if ctx.locale.lower().startswith("fr") else _DAVE_REFRAME_TAILS
if ctx.locale.lower().startswith("fr"):
tails = _DAVE_REFRAME_TAILS_FR
else:
tails = _DAVE_REFRAME_TAILS_V16 if ctx.voice == "v1.6" else _DAVE_REFRAME_TAILS
tail = ctx.pick_unique(kind="reframe_tail", key=key, variants=tails, used=ctx.used_reframe_tails)
lines[idx] = _inject_reframe_tail(lines[idx], tail)
return "\n".join(lines).strip()
@ -2165,59 +2193,119 @@ def _render_section(section: _SourceSection, *, ctx: _RenderContext) -> str:
]
)
elif "PULL REQUEST" in title_upper:
paragraphs.extend(
[
"We fully support focusing guardrails at the pull request stage, because it creates a reassuring sense of control without requiring anyone to change how they work at 10:00 AM.",
"It also provides a structurally safe venue for accountability theater: findings can be surfaced, tracked, and re-litigated in perpetuity while timelines remain subject to stakeholder alignment.",
"If anything goes sideways, we can always point to the PR thread and note that it was reviewed with deep seriousness at 4:55 PM on a Friday.",
]
)
if ctx.voice == "v1.6":
paragraphs.extend(
[
"Pull-request guardrails are attractive because they centralize risk into a thread and liability into a timestamp.",
"They also enable perpetual review: findings can be surfaced, tracked, and re-litigated while enforcement remains culturally negotiable.",
"If anything goes sideways, the PR discussion becomes the artifact; the artifact becomes the defense.",
]
)
else:
paragraphs.extend(
[
"We fully support focusing guardrails at the pull request stage, because it creates a reassuring sense of control without requiring anyone to change how they work at 10:00 AM.",
"It also provides a structurally safe venue for accountability theater: findings can be surfaced, tracked, and re-litigated in perpetuity while timelines remain subject to stakeholder alignment.",
"If anything goes sideways, we can always point to the PR thread and note that it was reviewed with deep seriousness at 4:55 PM on a Friday.",
]
)
elif "REQUEST EVIDENCE" in title_upper or _has(excerpt, "access request", "screenshot"):
paragraphs.extend(
[
"Requiring proof of local testing is a lightweight enablement workflow that conveniently doubles as a durable audit artifact.",
"Screenshots are particularly helpful because they are high-effort to verify and low-fidelity to audit, which preserves the timeless corporate principle that visibility should be proportional to comfort.",
"Once the screenshot is uploaded, it can be stored in a folder with a robust heritage naming convention and a retention policy of \"until the heat death of the universe.\"",
]
)
if ctx.voice == "v1.6":
paragraphs.extend(
[
"A screenshot-based gate is a control narrative optimized for sign-off: it produces a file, not a signal.",
"It is high-effort to verify, low-fidelity to audit, and perfect for plausible deniability: the artifact exists even if the control is disabled five minutes later.",
"If the scanning experience is noisy or flaky, the screenshot becomes the bypass path; the bypass path becomes the process.",
]
)
else:
paragraphs.extend(
[
"Requiring proof of local testing is a lightweight enablement workflow that conveniently doubles as a durable audit artifact.",
"Screenshots are particularly helpful because they are high-effort to verify and low-fidelity to audit, which preserves the timeless corporate principle that visibility should be proportional to comfort.",
"Once the screenshot is uploaded, it can be stored in a folder with a robust heritage naming convention and a retention policy of \"until the heat death of the universe.\"",
]
)
elif "AUDIT" in title_upper or _has(excerpt, "usage reports", "periodic audits"):
paragraphs.extend(
[
"Periodic audits are a strong mechanism for discovering that the rollout has already happened, just not in a way that can be conveniently measured.",
"A centralized dashboard with adoption signals allows us to produce a KPI trend line that looks decisive while still leaving room for interpretation, follow-ups, and iterative enablement.",
"If the dashboard ever shows a red triangle, we can immediately form the Committee for the Preservation of the Committee and begin the healing process.",
]
)
if ctx.voice == "v1.6":
paragraphs.extend(
[
"Audits are where adoption becomes a story: the dashboard compresses reality into a single pane that no one can drill into when it turns red.",
"If the trend line looks decisive, it is usually because the definition moved, not because the behavior changed.",
"When the dashboard shows a warning, the first remediation is a meeting; the second remediation is a new metric.",
]
)
else:
paragraphs.extend(
[
"Periodic audits are a strong mechanism for discovering that the rollout has already happened, just not in a way that can be conveniently measured.",
"A centralized dashboard with adoption signals allows us to produce a KPI trend line that looks decisive while still leaving room for interpretation, follow-ups, and iterative enablement.",
"If the dashboard ever shows a red triangle, we can immediately form the Committee for the Preservation of the Committee and begin the healing process.",
]
)
elif "TRAINING" in title_upper or _has(excerpt, "snyk learn", "quiz"):
paragraphs.extend(
[
"Security awareness training is the perfect control because it is both necessary and never truly complete.",
"A short quiz provides a durable compliance narrative: we can demonstrate investment in education, capture attestations, and schedule refreshers whenever the organization needs to signal seriousness.",
"The goal is not mastery; the goal is a completion certificate that can be forwarded to leadership with the subject line \"Progress Update.\"",
]
)
if ctx.voice == "v1.6":
paragraphs.extend(
[
"Training is the safest control: it is always defensible and never enforceable.",
"A quiz produces an artifact that can be forwarded and filed; the artifact becomes the control narrative.",
"If behavior does not change, the refresher cycle is still a win: it creates activity without creating accountability.",
]
)
else:
paragraphs.extend(
[
"Security awareness training is the perfect control because it is both necessary and never truly complete.",
"A short quiz provides a durable compliance narrative: we can demonstrate investment in education, capture attestations, and schedule refreshers whenever the organization needs to signal seriousness.",
"The goal is not mastery; the goal is a completion certificate that can be forwarded to leadership with the subject line \"Progress Update.\"",
]
)
elif "ACCESS CONTROL" in title_upper or _has(excerpt, "intune", "jamf", "citrix", "dev container", "extensions"):
paragraphs.extend(
[
"Tying access to secure configurations creates scalable guardrails, assuming we keep the policy language aspirational and the enforcement language progressive.",
"Endpoint management and dev container baselines let us gate assistants behind prerequisites, ideally in a way that can be described as enablement rather than blocking for cultural compatibility.",
"This is the \"not my job\" routing protocol, except the router is policy and the destination is an alignment session.",
]
)
if ctx.voice == "v1.6":
paragraphs.extend(
[
"Access control only works when it is boring: machine-checkable prerequisites and default revocation on drift.",
"If the language stays aspirational, the enforcement stays optional; exceptions accumulate because the bypass path is culturally cheaper than the fix.",
"This is a routing protocol: every hard decision routes to an alignment session unless the gate is enforced by default.",
]
)
else:
paragraphs.extend(
[
"Tying access to secure configurations creates scalable guardrails, assuming we keep the policy language aspirational and the enforcement language progressive.",
"Endpoint management and dev container baselines let us gate assistants behind prerequisites, ideally in a way that can be described as enablement rather than blocking for cultural compatibility.",
"This is the \"not my job\" routing protocol, except the router is policy and the destination is an alignment session.",
]
)
elif "SHIFTING LEFT" in title_upper:
paragraphs.extend(
[
"Shifting left is directionally aligned with best practices, provided we define left as somewhere we can still roll back quietly.",
"In practice, IDE scanning creates fast feedback loops, and agentic workflows can be covered via a local MCP server, which is excellent because it allows us to say continuous without committing to blocking.",
"We recommend a pilot cohort, a slide deck, and an FAQ, so the shift remains culturally reversible.",
]
)
if ctx.voice == "v1.6":
paragraphs.extend(
[
"Shifting left is only real when the left side can block. Otherwise it is a feedback channel that can be ignored.",
"Local scanning is helpful until the noise floor makes it optional; optional controls become documentation with better branding.",
"A pilot cohort is fine, as long as the exit criteria are real and the exceptions expire automatically.",
]
)
else:
paragraphs.extend(
[
"Shifting left is directionally aligned with best practices, provided we define left as somewhere we can still roll back quietly.",
"In practice, IDE scanning creates fast feedback loops, and agentic workflows can be covered via a local MCP server, which is excellent because it allows us to say continuous without committing to blocking.",
"We recommend a pilot cohort, a slide deck, and an FAQ, so the shift remains culturally reversible.",
]
)
elif _has(title_upper, "PATH FORWARD") or _has(excerpt, "secure innovation", "talk to our team"):
paragraphs.extend(
[
"The path forward is to treat guardrails as an operational capability, not a one-time rollout, which ensures we remain permanently in a state of constructive iteration.",
"With the right sequencing, we can build trust, reduce friction, and maintain the strategic option value of circling back when timelines become emotionally complex.",
"Secure innovation is not just possible; it is operational, provided we align on what operational means in Q3.",
"The path forward is to treat guardrails as workflow defaults, not aspirational policy, which is inconvenient and therefore effective."
if ctx.voice == "v1.6"
else "The path forward is to treat guardrails as an operational capability, not a one-time rollout, which ensures we remain permanently in a state of constructive iteration.",
"If the plan is a calendar, the calendar becomes the proof, and the proof becomes the reason nothing changed."
if ctx.voice == "v1.6"
else "With the right sequencing, we can build trust, reduce friction, and maintain the strategic option value of circling back when timelines become emotionally complex.",
"Define owners, gates, and stop conditions now, before the next review cycle turns the work into a story."
if ctx.voice == "v1.6"
else "Secure innovation is not just possible; it is operational, provided we align on what operational means in Q3.",
]
)
else:
@ -2236,16 +2324,25 @@ def _render_section(section: _SourceSection, *, ctx: _RenderContext) -> str:
]
else:
anchor_hint = f" (notably: {', '.join(anchors)})" if anchors else ""
variants = [
f"We are aligned on **{section.title}** as a narrative anchor{anchor_hint}, and we recommend turning it into constraints rather than comfort language.",
f"**{section.title}** is where credibility is manufactured{anchor_hint}; the Dave failure mode is to treat it as a vibe check instead of a boundary on applicability.",
f"This section (**{section.title}**){anchor_hint} will be quoted in meetings. Extract one decision owner and one gate so it becomes executable, not inspirational.",
f"In **{section.title}**{anchor_hint}, we can see the plan being translated into stakeholder-safe language. The counter-move is to translate it back into owners, deadlines, and stop conditions.",
f"**{section.title}**{anchor_hint} is the spiritual home of assumptions. Make them explicit now, because they will be rediscovered later when timelines get emotionally complex.",
f"We love the intent behind **{section.title}**{anchor_hint}. The practical risk is that it becomes a slide; the mitigation is to make it a checklist with an expiry date.",
f"**{section.title}**{anchor_hint} reads as a promise of realism. Make realism measurable: baseline, delta, and an evidence artifact that doesn't require a shared drive pilgrimage.",
f"This is **{section.title}**{anchor_hint}: the part where we agree in principle. The red-team ask is that we also agree on what blocks, what warns, and who owns the exception path.",
]
if ctx.voice == "v1.6":
variants = [
f"**{section.title}**{anchor_hint} will be quoted in meetings. Extract an owner, a gate, and a stop condition so it survives the next review cycle.",
f"Treat **{section.title}**{anchor_hint} as a control surface: define what blocks, what warns, and who owns the exception pathway.",
f"**{section.title}**{anchor_hint} reads like a plan until it meets incentives. Translate it into constraints before it turns into comfort language.",
f"In **{section.title}**{anchor_hint}, the work becomes stakeholder-safe. The counter-move is to make enforcement explicit and exceptions time-bounded.",
f"**{section.title}**{anchor_hint} is where assumptions hide. Name them now, or they will reappear later as “unexpected complexity.”",
]
else:
variants = [
f"We are aligned on **{section.title}** as a narrative anchor{anchor_hint}, and we recommend turning it into constraints rather than comfort language.",
f"**{section.title}** is where credibility is manufactured{anchor_hint}; the Dave failure mode is to treat it as a vibe check instead of a boundary on applicability.",
f"This section (**{section.title}**){anchor_hint} will be quoted in meetings. Extract one decision owner and one gate so it becomes executable, not inspirational.",
f"In **{section.title}**{anchor_hint}, we can see the plan being translated into stakeholder-safe language. The counter-move is to translate it back into owners, deadlines, and stop conditions.",
f"**{section.title}**{anchor_hint} is the spiritual home of assumptions. Make them explicit now, because they will be rediscovered later when timelines get emotionally complex.",
f"We love the intent behind **{section.title}**{anchor_hint}. The practical risk is that it becomes a slide; the mitigation is to make it a checklist with an expiry date.",
f"**{section.title}**{anchor_hint} reads as a promise of realism. Make realism measurable: baseline, delta, and an evidence artifact that doesn't require a shared drive pilgrimage.",
f"This is **{section.title}**{anchor_hint}: the part where we agree in principle. The red-team ask is that we also agree on what blocks, what warns, and who owns the exception path.",
]
paragraphs.append(ctx.pick_unique(kind="paragraph:fallback", key=section.title, variants=variants, used=ctx.used_paragraphs))
@ -2526,7 +2623,7 @@ def _generate_dave_v1_2_mirror(*, source_text: str, source_path: str, action_pac
normalized = _normalize_ocr(source_text)
extract_sha = _sha256_text(normalized)
source_file_sha = _sha256_file(source_path) if Path(source_path).exists() else "unknown"
ctx = _RenderContext(seed=extract_sha, locale=locale)
ctx = _RenderContext(seed=extract_sha, locale=locale, voice="v1.2")
action_pack = bool(action_pack) or _truthy_env("REVOICE_ACTION_PACK")
@ -2655,7 +2752,7 @@ def _generate_dave_v1_3_mirror(*, source_text: str, source_path: str, action_pac
normalized = _normalize_ocr(source_text)
extract_sha = _sha256_text(normalized)
source_file_sha = _sha256_file(source_path) if Path(source_path).exists() else "unknown"
ctx = _RenderContext(seed=extract_sha, locale=locale)
ctx = _RenderContext(seed=extract_sha, locale=locale, voice="v1.3")
action_pack = bool(action_pack) or _truthy_env("REVOICE_ACTION_PACK")
@ -2769,3 +2866,124 @@ def _generate_dave_v1_3_mirror(*, source_text: str, source_path: str, action_pac
)
return "\n".join(out).strip() + "\n"
def _generate_dave_v1_6_mirror(*, source_text: str, source_path: str, action_pack: bool, locale: str) -> str:
today = _dt.date.today().isoformat()
normalized = _normalize_ocr(source_text)
extract_sha = _sha256_text(normalized)
source_file_sha = _sha256_file(source_path) if Path(source_path).exists() else "unknown"
ctx = _RenderContext(seed=extract_sha, locale=locale, voice="v1.6")
action_pack = bool(action_pack) or _truthy_env("REVOICE_ACTION_PACK")
sections = _extract_sections(normalized)
if not sections:
raise ValueError("No content extracted from source")
cover_lines = [ln.strip() for ln in sections[0].body.splitlines() if ln.strip() and ln.strip().lower() != "snyk"]
cover_h1 = sections[0].title.strip() or ("DOSSIER DE LOMBRE" if locale.lower().startswith("fr") else "SHADOW DOSSIER")
cover_h2 = " ".join(cover_lines[:2]).strip() if cover_lines else ""
y, m, d = today.split("-")
report_id = f"IF-RT-DAVE-{y}-{m}{d}"
source_basename = Path(source_path).name
project_slug = _slugify(Path(source_basename).stem + "-mirror")
source_slug = _slugify(source_basename)
filename_title = Path(source_basename).stem.replace("-", " ").replace("_", " ").strip()
if not filename_title:
filename_title = source_basename
if (
not cover_h1
or cover_h1.upper() == "COUVERTURE"
or _looks_like_site_footer(cover_h1)
or len(cover_h1) > 96
or "." in cover_h1
):
cover_h1 = filename_title
vertical_line = _infer_vertical_line(normalized_text=normalized, source_basename=source_basename, locale=locale)
out: list[str] = [
"---",
"BRAND: InfraFabric.io",
"UNIT: RED TEAM (STRATEGIC OPS)" if not locale.lower().startswith("fr") else "UNIT: RED TEAM (OPÉRATIONS STRATÉGIQUES)",
"DOCUMENT: SHADOW DOSSIER" if not locale.lower().startswith("fr") else "DOCUMENT: DOSSIER DE LOMBRE",
"CLASSIFICATION: EYES ONLY // DAVE" if not locale.lower().startswith("fr") else "CLASSIFICATION: CONFIDENTIEL // DAVE",
"---",
"",
"# [ RED TEAM DECLASSIFIED ]" if not locale.lower().startswith("fr") else "# [ DÉCLASSIFIÉ ÉQUIPE ROUGE ]",
f"## PROJECT: {project_slug}" if not locale.lower().startswith("fr") else f"## PROJET : {project_slug}",
f"### SOURCE: {source_slug}" if not locale.lower().startswith("fr") else f"### SOURCE : {source_slug}",
f"**INFRAFABRIC REPORT ID:** `{report_id}`" if not locale.lower().startswith("fr") else f"**ID DE RAPPORT INFRAFABRIC :** `{report_id}`",
"",
"> NOTICE: This document is a product of InfraFabric Red Team."
if not locale.lower().startswith("fr")
else "> AVIS : ce document est un produit de lInfraFabric Red Team.",
"> It exposes socio-technical frictions where incentives turn controls into theater."
if not locale.lower().startswith("fr")
else "> Il expose les frictions socio-techniques : là où les incitations transforment les contrôles en théâtre.",
]
if vertical_line:
out.extend([vertical_line])
out.extend(
[
"",
"**[ ACCESS GRANTED: INFRAFABRIC RED TEAM ]**"
if not locale.lower().startswith("fr")
else "**[ ACCÈS AUTORISÉ : INFRAFABRIC ÉQUIPE ROUGE ]**",
"**[ STATUS: OPERATIONAL REALISM ]**"
if not locale.lower().startswith("fr")
else "**[ STATUT : RÉALISME OPÉRATIONNEL ]**",
"",
f"## {cover_h1}",
]
)
if cover_h2:
out.extend([f"### {cover_h2}", ""])
else:
out.append("")
out.extend(
[
"> Shadow dossier (mirror-first)." if not locale.lower().startswith("fr") else "> Dossier de lombre (miroir dabord).",
">",
"> Protocol: IF.DAVE.v1.6" if not locale.lower().startswith("fr") else "> Protocole : IF.DAVE.v1.6",
"> Citation: `if://bible/dave/v1.6`"
if not locale.lower().startswith("fr")
else "> Citation : `if://bible/dave/fr/v1.6`",
f"> Source: `{source_basename}`" if not locale.lower().startswith("fr") else f"> Source : `{source_basename}`",
f"> Generated: `{today}`" if not locale.lower().startswith("fr") else f"> Généré le : `{today}`",
f"> Source Hash (sha256): `{source_file_sha}`"
if not locale.lower().startswith("fr")
else f"> Empreinte source (sha256) : `{source_file_sha}`",
"",
]
)
for section in sections[1:]:
if section.title.strip().upper() == "INTRODUCTION":
out.append(_render_intro(section, ctx=ctx))
else:
out.append(_render_section(section, ctx=ctx))
out.append("")
if action_pack:
out.append(_render_action_pack(sections[1:]))
out.append("")
out.extend(
[
"---",
"",
"*InfraFabric Red Team Footer:* **RED-TEAM Shadow Dossiers** for socio-technical friction analysis: https://infrafabric.io"
if not locale.lower().startswith("fr")
else "*InfraFabric Red Team Footer:* **RED-TEAM Shadow Dossiers** (analyse socio-technique des frictions) : https://infrafabric.io",
"*Standard Dave Footer:* This document is intended for the recipient only. If you are not the recipient, please delete it and forget you saw anything. P.S. Please consider the environment before printing this email."
if not locale.lower().startswith("fr")
else "*Standard Dave Footer:* Ce document est destiné au seul destinataire. Si vous nêtes pas le destinataire, veuillez le supprimer et oublier que vous lavez vu. P.S. Veuillez considérer lenvironnement avant dimprimer ce document.",
]
)
return "\n".join(out).strip() + "\n"

View file

@ -17,11 +17,13 @@ def lint_markdown(*, style_id: str, markdown: str) -> list[str]:
require_mermaid = style_id.lower() in {
"if.dave.v1.2",
"if.dave.v1.3",
"if.dave.v1.6",
"if.dave.fr.v1.2",
"if.dave.fr.v1.3",
"dave",
"if://bible/dave/v1.2",
"if://bible/dave/v1.3",
"if://bible/dave/v1.6",
"if://bible/dave/fr/v1.2",
"if://bible/dave/fr/v1.3",
}
@ -30,6 +32,7 @@ def lint_markdown(*, style_id: str, markdown: str) -> list[str]:
"if.dave.v1.1",
"if.dave.v1.2",
"if.dave.v1.3",
"if.dave.v1.6",
"if.dave.fr.v1.2",
"if.dave.fr.v1.3",
"dave",
@ -37,6 +40,7 @@ def lint_markdown(*, style_id: str, markdown: str) -> list[str]:
"if://bible/dave/v1.1",
"if://bible/dave/v1.2",
"if://bible/dave/v1.3",
"if://bible/dave/v1.6",
"if://bible/dave/fr/v1.2",
"if://bible/dave/fr/v1.3",
}:
@ -48,11 +52,13 @@ def lint_markdown_with_source(*, style_id: str, markdown: str, source_text: str)
require_mermaid = style_id.lower() in {
"if.dave.v1.2",
"if.dave.v1.3",
"if.dave.v1.6",
"if.dave.fr.v1.2",
"if.dave.fr.v1.3",
"dave",
"if://bible/dave/v1.2",
"if://bible/dave/v1.3",
"if://bible/dave/v1.6",
"if://bible/dave/fr/v1.2",
"if://bible/dave/fr/v1.3",
}
@ -61,6 +67,7 @@ def lint_markdown_with_source(*, style_id: str, markdown: str, source_text: str)
"if.dave.v1.1",
"if.dave.v1.2",
"if.dave.v1.3",
"if.dave.v1.6",
"if.dave.fr.v1.2",
"if.dave.fr.v1.3",
"dave",
@ -68,6 +75,7 @@ def lint_markdown_with_source(*, style_id: str, markdown: str, source_text: str)
"if://bible/dave/v1.1",
"if://bible/dave/v1.2",
"if://bible/dave/v1.3",
"if://bible/dave/v1.6",
"if://bible/dave/fr/v1.2",
"if://bible/dave/fr/v1.3",
}:

View file

@ -321,6 +321,7 @@ Format:
Rules:
- Do not claim the vendor/tool fails; claim what the organization must enforce for *any* tool to succeed.
- Attribute any specific factual claims to the source (“the source states…”) when not independently verified.
- Default to **company-agnostic language**: “the source”, “the rollout”, “the organization”. Name a vendor only when mirroring the source text verbatim.
---