Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .cursor/rules/session-mode-keywords.mdc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
description: Operator shorthand — deps, feature, homelab, completao, lab-lessons, external-eval, docs, houseclean, backlog, pmo-view, release-ritual, safe-commit, pii-remediation-ritual, pii-fresh-audit, legal-dossier-update, es-find, x-pace-check, x-posted, social-today-check, operator-sli-check, sonar-mcp; scope the session (plans + scripts). homelab = HOMELAB_VALIDATION second environment + situational homelab-ssh-via-terminal.mdc (token homelab + HOMELAB* / lab-op / collect-homelab / homelab script globs). completao = LAB_COMPLETAO_RUNBOOK SSH orchestrate (lab-completao-orchestrate.ps1 -Privileged, wrappers, sudoers per LAB_OP_PRIVILEGED_COLLECTION; protect primary L-series dev PC per PRIMARY_WINDOWS_WORKSTATION_PROTECTION; no redundant permission pings per operator-direct-execution); doc+code capability matrix; automate repeat steps (TOKEN_AWARE_SCRIPTS_HUB); document timeouts, latency, FP/FN vs synthetic, real-path confidence in private session notes per lab-completao-workflow. lab-lessons = dated public LAB_LESSONS snapshots under docs/ops/lab_lessons_learned/ + hub LAB_LESSONS_LEARNED.md + PLANS_TODO bridges; situational lab-lessons-learned-archive.mdc; ADR 0042; pair with private COMPLETAO_SESSION*. external-eval = LAB_EXTERNAL_CONNECTIVITY_EVAL public APIs + optional read-only DBs + intentional failures; no secrets in git. pmo-view = list plan/PMO paths + Markdown preview shortcuts (agent cannot toggle Preview). safe-commit = read-only git snapshot + optional quick guard tests before Preview. block-close = work-block/lab-exit boundary (VC/carryover/private-stack; not full eod-sync). morning-readiness / carryover-sweep = operator-day-ritual.ps1 -Mode Morning + today-mode/README Morning readiness ladder (Tier A daily; -1/-1b/-1L cadence). pii-remediation-ritual = on-demand public-tree PII pass (PII_REMEDIATION_RITUAL.md; private seeds + HEAD order). pii-fresh-audit = Windows fresh-clone PII self-audit (pii-fresh-clone-audit.ps1). es-find = Windows Everything filename search (read situational everything-es-cli.mdc or @ first; es-find.ps1). x-pace-check / x-posted = X editorial pace + post validation (private SOCIAL_HUB + scripts/social-x-pace-remind.ps1). social-today-check = skim private SOCIAL_HUB + calendar for today/tomorrow planned posts (SOCIAL_PUBLISH_AND_TODAY_MODE.md). operator-sli-check = weekly LinkedIn SLI validation (private ATS/SLI + SOCIAL_HUB). private-stack-sync = stacked private repo close (read situational docs-private-workspace-context.mdc or @ first; private-git-sync.ps1 + PRIVATE_STACK_SYNC_RITUAL.md; agent-docs-private-read-access stays always-on for never self-block; no secrets in tracked files). release-ritual = public semver publish order (read situational release-publish-sequencing.mdc or @ first; docker-local-smoke-cleanup stays always-on): tag + GitHub + Docker smoke (lab) before Hub push + prune + Hub UI paste. Browser SSO anti-toil: for Hub/GitHub web UI, try cursor-ide-browser + Google SSO before any “may I access?” ping (operator-browser-warm-session, operator-direct-execution §5). legal-dossier-update = private legal/labour evidence intake per dossier-update-on-evidence.mdc (INDEX + SUMMARY + RETEACH + stacked private git); never name parties in tracked files. plans-status = situational plans-status-pl-sync (docs/feature/houseclean/backlog or plan globs or @). plans-archive = situational plans-archive-on-completion (plan/archive globs or @). sonar-mcp = situational sonarqube_mcp_instructions (sonar globs or @). study-cadence = situational study-cadence-reminders (study-check or @). Tier B (#1154) @ latches when globs miss: git-pr-sync=@git-pr-sync-before-advice.mdc; merge-autonomous=@agent-autonomous-merge-and-lab-ops.mdc; browser=@cursor-browser-social-sso-hygiene.mdc or @operator-browser-warm-session.mdc; scripts-wrapper=@repo-scripts-wrapper-ritual.mdc; session-ritual=@agent-session-ritual-sync-main-and-private-stack.mdc (carryover-sweep/eod-sync/private-stack-sync).
description: "Operator shorthand — deps, feature, homelab, completao, lab-lessons, external-eval, docs, houseclean, backlog, pmo-view, release-ritual, safe-commit, pii-remediation-ritual, pii-fresh-audit, legal-dossier-update, es-find, x-pace-check, x-posted, social-today-check, operator-sli-check, sonar-mcp; scope the session (plans + scripts). homelab = HOMELAB_VALIDATION second environment + situational homelab-ssh-via-terminal.mdc (token homelab + HOMELAB* / lab-op / collect-homelab / homelab script globs). completao = LAB_COMPLETAO_RUNBOOK SSH orchestrate (lab-completao-orchestrate.ps1 -Privileged, wrappers, sudoers per LAB_OP_PRIVILEGED_COLLECTION; protect primary L-series dev PC per PRIMARY_WINDOWS_WORKSTATION_PROTECTION; no redundant permission pings per operator-direct-execution); doc+code capability matrix; automate repeat steps (TOKEN_AWARE_SCRIPTS_HUB); document timeouts, latency, FP/FN vs synthetic, real-path confidence in private session notes per lab-completao-workflow. lab-lessons = dated public LAB_LESSONS snapshots under docs/ops/lab_lessons_learned/ + hub LAB_LESSONS_LEARNED.md + PLANS_TODO bridges; situational lab-lessons-learned-archive.mdc; ADR 0042; pair with private COMPLETAO_SESSION*. external-eval = LAB_EXTERNAL_CONNECTIVITY_EVAL public APIs + optional read-only DBs + intentional failures; no secrets in git. pmo-view = list plan/PMO paths + Markdown preview shortcuts (agent cannot toggle Preview). safe-commit = read-only git snapshot + optional quick guard tests before Preview. block-close = work-block/lab-exit boundary (VC/carryover/private-stack; not full eod-sync). morning-readiness / carryover-sweep = operator-day-ritual.ps1 -Mode Morning + today-mode/README Morning readiness ladder (Tier A daily; -1/-1b/-1L cadence). pii-remediation-ritual = on-demand public-tree PII pass (PII_REMEDIATION_RITUAL.md; private seeds + HEAD order). pii-fresh-audit = Windows fresh-clone PII self-audit (pii-fresh-clone-audit.ps1). es-find = Windows Everything filename search (read situational everything-es-cli.mdc or @ first; es-find.ps1). x-pace-check / x-posted = X editorial pace + post validation (private SOCIAL_HUB + scripts/social-x-pace-remind.ps1). social-today-check = skim private SOCIAL_HUB + calendar for today/tomorrow planned posts (SOCIAL_PUBLISH_AND_TODAY_MODE.md). operator-sli-check = weekly LinkedIn SLI validation (private ATS/SLI + SOCIAL_HUB). private-stack-sync = stacked private repo close (read situational docs-private-workspace-context.mdc or @ first; private-git-sync.ps1 + PRIVATE_STACK_SYNC_RITUAL.md; agent-docs-private-read-access stays always-on for never self-block; no secrets in tracked files). release-ritual = public semver publish order (read situational release-publish-sequencing.mdc or @ first; docker-local-smoke-cleanup stays always-on): tag + GitHub + Docker smoke (lab) before Hub push + prune + Hub UI paste. Browser SSO anti-toil: for Hub/GitHub web UI, try cursor-ide-browser + Google SSO before any “may I access?” ping (operator-browser-warm-session, operator-direct-execution §5). legal-dossier-update = private legal/labour evidence intake per dossier-update-on-evidence.mdc (INDEX + SUMMARY + RETEACH + stacked private git); never name parties in tracked files. plans-status = situational plans-status-pl-sync (docs/feature/houseclean/backlog or plan globs or @). plans-archive = situational plans-archive-on-completion (plan/archive globs or @). sonar-mcp = situational sonarqube_mcp_instructions (sonar globs or @). study-cadence = situational study-cadence-reminders (study-check or @). Tier B (#1154) @ latches when globs miss: git-pr-sync=@git-pr-sync-before-advice.mdc; merge-autonomous=@agent-autonomous-merge-and-lab-ops.mdc; browser=@cursor-browser-social-sso-hygiene.mdc or @operator-browser-warm-session.mdc; scripts-wrapper=@repo-scripts-wrapper-ritual.mdc; session-ritual=@agent-session-ritual-sync-main-and-private-stack.mdc (carryover-sweep/eod-sync/private-stack-sync)."
globs: ["**/*"]
alwaysApply: true
---
Expand Down
2 changes: 1 addition & 1 deletion .cursor/rules/windows-pcloud-drive-search-discipline.mdc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
description: Windows dev PC — pCloud on P: (often); default filename/path search on large trees = scripts/es-find.ps1 (es.exe); if es fails notify operator then native fallback (e.g. capped Get-ChildItem). Avoid opening with unbounded -Recurse on huge sync roots.
description: "Windows dev PC — pCloud on P: (often); default filename/path search on large trees = scripts/es-find.ps1 (es.exe); if es fails notify operator then native fallback (e.g. capped Get-ChildItem). Avoid opening with unbounded -Recurse on huge sync roots."
globs: ["**/*"]
alwaysApply: true
---
Expand Down
78 changes: 77 additions & 1 deletion tests/test_markdown_lint.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
- MD060: Fenced code blocks use consistent marker (all ``` or all ~~~)
- MD031: Blanks around fences (blank line before/after fenced code blocks)
- MD034: No bare URLs (wrap in angle brackets or use [text](url); skip inside code blocks)
- FRONTMATTER01: Leading ``---`` must close within ~40 lines and parse as a YAML mapping
- Table (compact): No space to the left of pipe (e.g. |col not | col)

Excludes: paths in MARKDOWN_LINT_EXCLUDE only (.git, node_modules, .venv, **private**, etc.) by default.
Expand All @@ -22,6 +23,8 @@
import subprocess
from pathlib import Path

import yaml


def _project_root() -> Path:
return Path(__file__).resolve().parent.parent
Expand Down Expand Up @@ -344,6 +347,48 @@ def _check_table_pipe_space(_text: str) -> list[tuple[int, str]]:
return []


# --- FRONTMATTER01: leading --- must close and parse as YAML mapping ---
_FRONTMATTER_CLOSE_WINDOW = 40
_FRONTMATTER01_MSG = "FRONTMATTER01: leading --- without valid closing YAML frontmatter"


def _check_frontmatter_integrity(text: str) -> list[tuple[int, str]]:
"""If the first non-empty line is ``---``, require a closing ``---`` soon and a YAML dict.

Catches the ISSUE_QUEUE_SEQUENCING_MAP class of bug: a lone leading ``---``
is treated as YAML frontmatter by preview tools and then chokes on Markdown
(e.g. ``**bold**``) before any closing fence.
"""
lines = text.splitlines()
start_idx: int | None = None
for i, line in enumerate(lines):
if line.strip() == "":
continue
if line.strip() == "---":
start_idx = i
break
if start_idx is None:
return []

end_idx: int | None = None
window_end = min(len(lines), start_idx + 1 + _FRONTMATTER_CLOSE_WINDOW)
for j in range(start_idx + 1, window_end):
if lines[j].strip() == "---":
end_idx = j
break
if end_idx is None:
return [(start_idx + 1, _FRONTMATTER01_MSG)]

body = "\n".join(lines[start_idx + 1 : end_idx])
try:
loaded = yaml.safe_load(body) if body.strip() else None
except yaml.YAMLError:
return [(start_idx + 1, _FRONTMATTER01_MSG)]
if not isinstance(loaded, dict):
return [(start_idx + 1, _FRONTMATTER01_MSG)]
return []


def _lint_one(path: Path) -> list[tuple[int, str]]:
"""Run all checks on one file. Returns list of (line, message)."""
text = _read_md(path)
Expand All @@ -356,13 +401,44 @@ def _lint_one(path: Path) -> list[tuple[int, str]]:
out.extend(_check_md060(text))
out.extend(_check_md031(text))
out.extend(_check_md034(text))
out.extend(_check_frontmatter_integrity(text))
out.extend(_check_table_pipe_space(text))
return out


def test_frontmatter01_valid_skill_style_passes() -> None:
"""SKILL.md-style closed frontmatter must not violate FRONTMATTER01."""
text = "---\nname: example\ndescription: unit test fixture\n---\n\n# Body\n"
assert _check_frontmatter_integrity(text) == []


def test_frontmatter01_broken_leading_hr_fails() -> None:
"""Lone leading --- then Markdown (e.g. **field**) must violate FRONTMATTER01."""
text = "---\n# Title\n**Campo:** valor\n\nMore body.\n"
violations = _check_frontmatter_integrity(text)
assert violations
assert violations[0][0] == 1
assert "FRONTMATTER01" in violations[0][1]


def test_frontmatter01_no_leading_fence_is_inert() -> None:
"""Files without a leading --- are out of scope for FRONTMATTER01."""
text = "# Title\n\n**Campo:** valor\n"
assert _check_frontmatter_integrity(text) == []


def test_frontmatter01_closed_but_invalid_yaml_fails() -> None:
"""Closing --- with non-mapping / invalid YAML between fences still fails."""
text = "---\n**Campo:** valor\n---\n\n# Body\n"
violations = _check_frontmatter_integrity(text)
assert violations
assert "FRONTMATTER01" in violations[0][1]


def test_markdown_lint_no_violations(include_private_lint: bool):
"""
All project .md files (except excluded dirs) pass MD009, MD012, MD024, MD036, MD051, MD060, MD031, MD034.
All project .md files (except excluded dirs) pass MD009, MD012, MD024, MD036,
MD051, MD060, MD031, MD034, FRONTMATTER01.
Table style: SonarQube MD060 (aligned) is enforced via scripts/fix_markdown_sonar.py; test does not check table pipes.

SonarQube / markdownlint-style quality so CI catches regressions.
Expand Down