diff --git a/.cursor/rules/session-mode-keywords.mdc b/.cursor/rules/session-mode-keywords.mdc index 8a618a9c9..25419d708 100644 --- a/.cursor/rules/session-mode-keywords.mdc +++ b/.cursor/rules/session-mode-keywords.mdc @@ -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 --- diff --git a/.cursor/rules/windows-pcloud-drive-search-discipline.mdc b/.cursor/rules/windows-pcloud-drive-search-discipline.mdc index 5120b1417..52af51b72 100644 --- a/.cursor/rules/windows-pcloud-drive-search-discipline.mdc +++ b/.cursor/rules/windows-pcloud-drive-search-discipline.mdc @@ -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 --- diff --git a/tests/test_markdown_lint.py b/tests/test_markdown_lint.py index 8fd62b510..892927c7b 100644 --- a/tests/test_markdown_lint.py +++ b/tests/test_markdown_lint.py @@ -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. @@ -22,6 +23,8 @@ import subprocess from pathlib import Path +import yaml + def _project_root() -> Path: return Path(__file__).resolve().parent.parent @@ -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) @@ -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.