From b4531f0a4c0959b702268af5a71936bd7d625af0 Mon Sep 17 00:00:00 2001 From: Sami Assiri Date: Thu, 16 Apr 2026 16:46:36 +0300 Subject: [PATCH] feat(tier1): docs-governance CI, evidence gate, closure artifacts, trust/execution docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace repo-preflight with docs-governance workflow and check_docs_links.py - Class B bundle: require correlation_id for external_*; AuditMetadata trace fields - Root-safe TIER1 §2; optional .githooks pre-push for main - Add RELEASE_READINESS_MATRIX_AR, SOURCE_OF_TRUTH_INDEX, operational severity, external index - ExecWeeklyGovernanceContract; expand trust-fabric, execution-fabric, ADR-0001, ws5, Saudi overlays - Wire MASTER TOC, enterprise-readiness, completion-program, architecture_brief paths Made-with: Cursor --- .githooks/README.md | 42 ++++++++++ .githooks/pre-push | 20 +++++ ...repo-preflight.yml => docs-governance.yml} | 24 +++++- MASTER_OPERATING_PROMPT.md | 2 + docs/RELEASE_READINESS_MATRIX_AR.md | 23 ++++++ docs/SOURCE_OF_TRUTH_INDEX.md | 18 +++++ docs/TIER1_MASTER_CLOSURE_CHECKLIST_AR.md | 10 ++- .../adr/0001-tier1-execution-policy-spikes.md | 12 +++ docs/completion-program-workstreams.md | 3 +- docs/enterprise-readiness.md | 4 +- docs/executive-room-completion-spec.md | 5 ++ docs/github-enterprise-delivery-completion.md | 4 + docs/governance/approval-policy.md | 18 +++++ docs/governance/execution-fabric.md | 20 +++++ docs/governance/operational-severity-model.md | 18 +++++ .../pdpl-nca-ai-control-matrices.md | 22 ++++- .../saudi-compliance-and-ai-governance.md | 12 +++ docs/governance/trust-fabric.md | 19 +++++ docs/references/tier1-external-index.md | 23 ++++++ docs/ws5-connector-events-metrics.md | 26 ++++++ .../backend/app/api/v1/approval_center.py | 4 +- .../backend/app/schemas/structured_outputs.py | 22 +++++ .../app/services/core_os/decision_memo.py | 5 ++ .../core_os/decision_plane_contracts.py | 9 ++- .../tests/test_decision_plane_contracts.py | 42 ++++++++++ scripts/architecture_brief.py | 7 ++ scripts/check_docs_links.py | 81 +++++++++++++++++++ 27 files changed, 480 insertions(+), 15 deletions(-) create mode 100644 .githooks/README.md create mode 100644 .githooks/pre-push rename .github/workflows/{repo-preflight.yml => docs-governance.yml} (50%) create mode 100644 docs/RELEASE_READINESS_MATRIX_AR.md create mode 100644 docs/SOURCE_OF_TRUTH_INDEX.md create mode 100644 docs/governance/operational-severity-model.md create mode 100644 docs/references/tier1-external-index.md create mode 100644 scripts/check_docs_links.py diff --git a/.githooks/README.md b/.githooks/README.md new file mode 100644 index 00000000..6fc17b41 --- /dev/null +++ b/.githooks/README.md @@ -0,0 +1,42 @@ +# Git hooks (اختياري — Root-safe) + +**مصدر الحقيقة للامتثال:** CI في `.github/workflows/docs-governance.yml` (وليس هذا المجلد). + +## الهدف + +تذكير محلي بأن أوامر الحوكمة تُشغَّل من **جذر الريبو** (`cwd` = المجلد الذي يحتوي `scripts/` و`docs/`). + +## تفعيل pre-push (فرع `main` فقط) + +من جذر الريبو: + +```bash +git config core.hooksPath .githooks +``` + +انسخ أو أنشئ ملف `.githooks/pre-push` (تنفيذي) بالمحتوى التالي: + +```bash +#!/usr/bin/env bash +set -euo pipefail + +protected_branch="refs/heads/main" +while read local_ref local_sha remote_ref remote_sha; do + if [[ "$remote_ref" != "$protected_branch" ]]; then + continue + fi + repo_root="$(git rev-parse --show-toplevel)" + cd "$repo_root" + if command -v python3 >/dev/null 2>&1; then + python3 scripts/architecture_brief.py + elif command -v py >/dev/null 2>&1; then + py -3 scripts/architecture_brief.py + else + echo "pre-push: لا يوجد python3/py — تخطي architecture_brief" + fi +done +``` + +ثم (على Unix): `chmod +x .githooks/pre-push`. + +على Windows يمكن استخدام Git Bash أو WSL لتشغيل نفس السكربت. diff --git a/.githooks/pre-push b/.githooks/pre-push new file mode 100644 index 00000000..39eea928 --- /dev/null +++ b/.githooks/pre-push @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +# Optional: run only when pushing to main. CI remains source of truth. +set -euo pipefail + +protected_branch="refs/heads/main" + +while read -r local_ref local_sha remote_ref remote_sha; do + if [[ "${remote_ref:-}" != "$protected_branch" ]]; then + continue + fi + repo_root="$(git rev-parse --show-toplevel)" + cd "$repo_root" + if command -v python3 >/dev/null 2>&1; then + python3 scripts/architecture_brief.py + elif command -v py >/dev/null 2>&1; then + py -3 scripts/architecture_brief.py + else + echo "pre-push: python3/py not found; skipping architecture_brief" >&2 + fi +done diff --git a/.github/workflows/repo-preflight.yml b/.github/workflows/docs-governance.yml similarity index 50% rename from .github/workflows/repo-preflight.yml rename to .github/workflows/docs-governance.yml index babab3fe..bba4582a 100644 --- a/.github/workflows/repo-preflight.yml +++ b/.github/workflows/docs-governance.yml @@ -1,5 +1,5 @@ -# Preflight when repo-level governance / scripts change (no salesflow-saas code required) -name: Repo preflight +# Tier-1 P0: governance + docs integrity (fast, no salesflow-saas backend deps) +name: Docs governance on: push: @@ -7,29 +7,45 @@ on: paths: - "docs/**" - "scripts/architecture_brief.py" + - "scripts/check_docs_links.py" + - ".github/workflows/docs-governance.yml" - "MASTER_OPERATING_PROMPT.md" - "AGENTS.md" - "CLAUDE.md" - "Execution_Matrix.md" - "Execution_Matrix_v2.md" + - "Architecture_Pack.md" pull_request: branches: [main] paths: - "docs/**" - "scripts/architecture_brief.py" + - "scripts/check_docs_links.py" + - ".github/workflows/docs-governance.yml" - "MASTER_OPERATING_PROMPT.md" - "AGENTS.md" - "CLAUDE.md" - "Execution_Matrix.md" - "Execution_Matrix_v2.md" + - "Architecture_Pack.md" jobs: architecture_brief: + name: Constitution path check runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: "3.12" - - name: Architecture brief (constitution paths) - run: python scripts/architecture_brief.py + - run: python scripts/architecture_brief.py + + docs_links: + name: Markdown internal links + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - run: python scripts/check_docs_links.py diff --git a/MASTER_OPERATING_PROMPT.md b/MASTER_OPERATING_PROMPT.md index a189e43a..f4c5bac5 100644 --- a/MASTER_OPERATING_PROMPT.md +++ b/MASTER_OPERATING_PROMPT.md @@ -27,6 +27,8 @@ Deep-dive topics live under [`docs/governance/`](docs/governance/) (keep this fi | Master architecture blueprint (index) | [`docs/blueprint-master-architecture.md`](docs/blueprint-master-architecture.md) | | 90-day Tier-1 execution matrix | [`docs/execution-matrix-90d-tier1.md`](docs/execution-matrix-90d-tier1.md) | | Enterprise readiness (B2B checklist) | [`docs/enterprise-readiness.md`](docs/enterprise-readiness.md) | +| Release readiness matrix (AR) | [`docs/RELEASE_READINESS_MATRIX_AR.md`](docs/RELEASE_READINESS_MATRIX_AR.md) | +| Source of truth index (canonical vs shadow) | [`docs/SOURCE_OF_TRUTH_INDEX.md`](docs/SOURCE_OF_TRUTH_INDEX.md) | | Completion Program (8 workstreams) | [`docs/completion-program-workstreams.md`](docs/completion-program-workstreams.md) | | Architecture register (subsystem status) | [`docs/architecture-register.md`](docs/architecture-register.md) | | ADR: Execution matrix canonical (v1 vs v2) | [`docs/adr/0002-execution-matrix-canonical-source.md`](docs/adr/0002-execution-matrix-canonical-source.md) | diff --git a/docs/RELEASE_READINESS_MATRIX_AR.md b/docs/RELEASE_READINESS_MATRIX_AR.md new file mode 100644 index 00000000..e60cef1e --- /dev/null +++ b/docs/RELEASE_READINESS_MATRIX_AR.md @@ -0,0 +1,23 @@ +# مصفوفة جاهزية الإصدار (Tier-1) — Release Readiness Matrix + +**الغرض:** صف واحد (أو صف أسبوعي) لكل **مرشح إصدار (RC)** يربط الأبعاد التشغيلية بالدليل والمالك. +**مرجع:** [`enterprise-readiness.md`](enterprise-readiness.md)، [`TIER1_MASTER_CLOSURE_CHECKLIST_AR.md`](TIER1_MASTER_CLOSURE_CHECKLIST_AR.md)، [`governance/pdpl-nca-ai-control-matrices.md`](governance/pdpl-nca-ai-control-matrices.md) (بوابة enterprise). + +## قالب الجدول (انسخ صفًا لكل RC) + +| البُعد | الحالة | الدليل | المالك | +|--------|--------|--------|--------| +| **docs truth** (مصدر واحد) | | [`SOURCE_OF_TRUTH_INDEX.md`](SOURCE_OF_TRUTH_INDEX.md) + CI `docs-governance` | Tech Writer | +| **schema adherence** (Pydantic / عقود) | | `pytest` على `schemas` + مسارات Class B | Backend | +| **approval SLA** (Class B) | | طوابير / API موافقات + سجلات | Governance | +| **contradiction backlog** (ledger) | | [`trust/ledger-vs-tool-verification.md`](trust/ledger-vs-tool-verification.md) | Trust | +| **connector health** (واجهات) | | [`ws5-connector-events-metrics.md`](ws5-connector-events-metrics.md) | Integrations | +| **security checklist** | | `verify-launch` + [`LAUNCH_CHECKLIST.md`](../salesflow-saas/docs/LAUNCH_CHECKLIST.md) | Security | +| **Saudi controls** (PDPL/NCA/AI) | | [`governance/pdpl-nca-ai-control-matrices.md`](governance/pdpl-nca-ai-control-matrices.md) | Compliance | +| **provenance** (commit SHA / build) | | Git tag + artifact CI | Release | + +**حالات مقترحة للعمود «الحالة»:** `OK` | `Risk` | `Blocked` — مع أعلى [`operational-severity-model.md`](governance/operational-severity-model.md) مفتوحة في الملاحظات. + +## بوابة جودة البيانات (Great Expectations) + +عند تفعيل GE: اربط **checkpoint** ناجحًا بصف «schema adherence» أو صف فرعي «data quality»؛ لا تعتبر GE مجرد ملحق Data plane — انظر [`ws5-connector-events-metrics.md`](ws5-connector-events-metrics.md). diff --git a/docs/SOURCE_OF_TRUTH_INDEX.md b/docs/SOURCE_OF_TRUTH_INDEX.md new file mode 100644 index 00000000..e413935f --- /dev/null +++ b/docs/SOURCE_OF_TRUTH_INDEX.md @@ -0,0 +1,18 @@ +# فهرس مصدر الحقيقة (Canonical vs Shadow) + +**الغرض:** تقليل **ازدواجية** المسارات بين `docs/` جذر الريبو و[`salesflow-saas/docs/`](../salesflow-saas/docs/) عبر جدول صريح: أي موضوع يُعتبر **canonical**، وأين تبقى نسخ **legacy / shadow** للمرجع فقط. + +| الموضوع | Canonical (مصدر الحقيقة) | Shadow / legacy | المالك | دورة المراجعة | +|---------|---------------------------|-----------------|--------|----------------| +| دستور التشغيل للوكلاء | [`MASTER_OPERATING_PROMPT.md`](../MASTER_OPERATING_PROMPT.md) | [`AGENTS.md`](../AGENTS.md)، [`CLAUDE.md`](../CLAUDE.md) (ملخصات) | Architect | عند تغيير حوكمة رئيسية | +| إغلاق Tier-1 (عربي) | [`TIER1_MASTER_CLOSURE_CHECKLIST_AR.md`](TIER1_MASTER_CLOSURE_CHECKLIST_AR.md) | — | Program | كل أسبوعين مع السجل | +| إغلاق Tier-1 (50 بندًا EN) | [`salesflow-saas/docs/tier1-master-closure-checklist.md`](../salesflow-saas/docs/tier1-master-closure-checklist.md) | — | Program | مع PR إغلاق | +| سجل الأنظمة الفرعية | [`architecture-register.md`](architecture-register.md) | تكرار حالة في checklists طالما عمود واحد للحالة | Platform | أسبوعيًا | +| مصفوفة التنفيذ | [`adr/0002-execution-matrix-canonical-source.md`](adr/0002-execution-matrix-canonical-source.md) + الملف الذي يحدده الـ ADR | نسخ قديمة بأسماء متعددة إن وُجدت | PMO | عند إعادة تسمية المصفوفة | +| حوكمة الموصلات / Data plane | [`governance/connectors-and-data-plane.md`](governance/connectors-and-data-plane.md) | [`ws5-connector-events-metrics.md`](ws5-connector-events-metrics.md) (تفاصيل WS5) | Integrations | مع كل موصل جديد | +| أحداث وعقود | [`governance/events-and-schema.md`](governance/events-and-schema.md) | حقول **CloudEvents** الخارجية — انظر الملحق أدناه | Platform | عند تغيير الحدث | +| جاهزية الإصدار (RC) | [`RELEASE_READINESS_MATRIX_AR.md`](RELEASE_READINESS_MATRIX_AR.md) | — | Release | كل RC | + +## ملحق: مراجع خارجية (تجمع هنا لتقليل rot) + +انظر [`references/tier1-external-index.md`](references/tier1-external-index.md). diff --git a/docs/TIER1_MASTER_CLOSURE_CHECKLIST_AR.md b/docs/TIER1_MASTER_CLOSURE_CHECKLIST_AR.md index 94a8d44f..d1012bed 100644 --- a/docs/TIER1_MASTER_CLOSURE_CHECKLIST_AR.md +++ b/docs/TIER1_MASTER_CLOSURE_CHECKLIST_AR.md @@ -29,12 +29,14 @@ --- -## §2 سلامة الريبو والأوامر +## §2 سلامة الريبو والأوامر (Root-safe) | # | البند | الحالة | الدليل في الريبو | المالك | معيار الخروج | |---|--------|--------|-------------------|--------|---------------| -| 2.1 | أوامر من جذر الريبو | Pilot | [`scripts/architecture_brief.py`](../scripts/architecture_brief.py) + CI | DevEx | `architecture_brief` في CI | +| 2.0 | **أول خطوة:** `cwd` = جذر الريبو (مسار المستودع الذي يحتوي `scripts/` و`docs/`) قبل أي أمر أو سكربت | Pilot | هذا القسم + [`scripts/architecture_brief.py`](../scripts/architecture_brief.py) | DevEx | لا تشغيل من مجلدات فرعية بلا `PYTHONPATH`/مسارات صريحة | +| 2.1 | أوامر من جذر الريبو | Pilot | [`scripts/architecture_brief.py`](../scripts/architecture_brief.py) + CI | DevEx | `architecture_brief` في CI (`docs-governance`) | | 2.2 | توافق أوامر Cursor/Claude | DocOnly | [`.cursor/commands/`](../.cursor/commands/) + [`CLAUDE.md`](../CLAUDE.md) | AI Platform | جدول تطابق في [`governance/discovery-and-output-checklist.md`](governance/discovery-and-output-checklist.md) | +| 2.3 | Hook اختياري pre-push للفرع الحساس | DocOnly | [`.githooks/README.md`](../.githooks/README.md) | DevEx | **مصدر الحقيقة = CI**؛ الـ hook تكميلي فقط | --- @@ -54,7 +56,7 @@ |---|--------|--------|-------------------|--------|---------------| | 4.1 | مخططات منظمة (17 نوعًا) | Production | [`salesflow-saas/backend/app/schemas/structured_outputs.py`](../salesflow-saas/backend/app/schemas/structured_outputs.py) | AI Lead | Pydantic يمر | | 4.2 | حزمة قرار موحّدة | Production | [`decision_plane_contracts.py`](../salesflow-saas/backend/app/services/core_os/decision_plane_contracts.py) | Backend | مفاتيح bundle كاملة | -| 4.3 | فرض مسار Class B | Pilot | `GET /api/v1/approval-center/class-b-decision-bundle` | AI Lead | استجابة = bundle + اختبار | +| 4.3 | فرض مسار Class B + **correlation لـ external** | Pilot | `GET /api/v1/approval-center/class-b-decision-bundle` + `validate_class_b_bundle` | AI Lead | `external_*` بدون `correlation_id` = رفض؛ راجع [`approval-policy.md`](governance/approval-policy.md) | --- @@ -94,7 +96,7 @@ |---|--------|--------|-------------------|--------|---------------| | 8.1 | قائمة تسليم GitHub/OIDC | DocOnly | [`github-enterprise-delivery-completion.md`](github-enterprise-delivery-completion.md) | DevOps | rulesets موثّقة | | 8.2 | CI يغطي التطبيق | Production | [`.github/workflows/dealix-ci.yml`](../.github/workflows/dealix-ci.yml) | Platform | pytest + frontend | -| 8.3 | CI preflight للوثائق | Pilot | [`.github/workflows/repo-preflight.yml`](../.github/workflows/repo-preflight.yml) | DevEx | عند تغيير docs/scripts | +| 8.3 | CI حوكمة الوثائق (P0) | Pilot | [`.github/workflows/docs-governance.yml`](../.github/workflows/docs-governance.yml) | DevEx | `architecture_brief` + `check_docs_links` | --- diff --git a/docs/adr/0001-tier1-execution-policy-spikes.md b/docs/adr/0001-tier1-execution-policy-spikes.md index 14f01c1a..e7281248 100644 --- a/docs/adr/0001-tier1-execution-policy-spikes.md +++ b/docs/adr/0001-tier1-execution-policy-spikes.md @@ -59,3 +59,15 @@ Each spike produces: design note, threat assumptions, test commands and results, - [`../execution-matrix-90d-tier1.md`](../execution-matrix-90d-tier1.md) Phase 0–1 - [`../blueprint-master-architecture.md`](../blueprint-master-architecture.md) + +--- + +## Annex — Temporal operational criteria (high-risk paths) + +When Temporal moves from *Planned* to *Pilot* for **high-risk** workflow types (money movement, regulated messaging, cross-tenant admin): + +1. **Pinned workflow types** — register each workflow type + task queue in the service catalog; no anonymous “catch-all” workers for governed domains. +2. **Rollback rehearsal** — run a **simulated rollback** (cancel + compensation or documented manual playbook) in staging before every production worker upgrade that changes workflow code. +3. **Worker versioning** — adopt Temporal’s worker versioning policy before expanding beyond the pilot; track preview/beta notes in [`../references/tier1-external-index.md`](../references/tier1-external-index.md) (Temporal changelog link). + +These criteria are **release gate** rows in [`../RELEASE_READINESS_MATRIX_AR.md`](../RELEASE_READINESS_MATRIX_AR.md) when Temporal is claimed for a candidate build. diff --git a/docs/completion-program-workstreams.md b/docs/completion-program-workstreams.md index dbb55eb2..c5ca0f3a 100644 --- a/docs/completion-program-workstreams.md +++ b/docs/completion-program-workstreams.md @@ -6,7 +6,8 @@ **Living registers:** [`architecture-register.md`](architecture-register.md) (subsystem status), [`adr/0002-execution-matrix-canonical-source.md`](adr/0002-execution-matrix-canonical-source.md) (matrix source of truth). **PR #16 closure bundle (merged):** [`salesflow-saas/docs/tier1-master-closure-checklist.md`](../salesflow-saas/docs/tier1-master-closure-checklist.md) (50-item master gates) + supporting tracks under [`salesflow-saas/docs/`](../salesflow-saas/docs/) and [`salesflow-saas/docs/governance/`](../salesflow-saas/docs/governance/) — use alongside this index; prefer **one** status column between the register and the master checklist to avoid drift. -**Arabic master index (15 sections):** [`TIER1_MASTER_CLOSURE_CHECKLIST_AR.md`](TIER1_MASTER_CLOSURE_CHECKLIST_AR.md). +**Arabic master index (15 sections):** [`TIER1_MASTER_CLOSURE_CHECKLIST_AR.md`](TIER1_MASTER_CLOSURE_CHECKLIST_AR.md). +**فهرس مصدر الحقيقة:** [`SOURCE_OF_TRUTH_INDEX.md`](SOURCE_OF_TRUTH_INDEX.md) (canonical vs shadow). | WS | Name | SLA (target) | Primary deliverable docs / code | |----|------|--------------|-----------------------------------| diff --git a/docs/enterprise-readiness.md b/docs/enterprise-readiness.md index f3e135e3..133c6a59 100644 --- a/docs/enterprise-readiness.md +++ b/docs/enterprise-readiness.md @@ -14,7 +14,9 @@ This checklist helps **internal teams** prepare for **B2B / enterprise** convers 8. [`completion-program-workstreams.md`](completion-program-workstreams.md) — eight workstreams from constitution to production. 9. [`architecture-register.md`](architecture-register.md) — subsystem status snapshot. 10. [`TIER1_MASTER_CLOSURE_CHECKLIST_AR.md`](TIER1_MASTER_CLOSURE_CHECKLIST_AR.md) — إغلاق Tier-1 (عربي) + [`salesflow-saas/docs/tier1-master-closure-checklist.md`](../salesflow-saas/docs/tier1-master-closure-checklist.md) (50 بندًا). -11. [`governance/pdpl-nca-ai-control-matrices.md`](governance/pdpl-nca-ai-control-matrices.md) — **بوابة إصدار enterprise:** اتبع قسم «Enterprise release gate» قبل وسم الإصدار. +11. [`SOURCE_OF_TRUTH_INDEX.md`](SOURCE_OF_TRUTH_INDEX.md) — مصدر واحد لكل موضوع (تقليل drift بين `docs/` و`salesflow-saas/docs/`). +12. [`RELEASE_READINESS_MATRIX_AR.md`](RELEASE_READINESS_MATRIX_AR.md) — صف لكل RC: docs truth، موصلات، أمان، سعودي، provenance. +13. [`governance/pdpl-nca-ai-control-matrices.md`](governance/pdpl-nca-ai-control-matrices.md) — **بوابة إصدار enterprise:** اتبع قسم «Enterprise release gate» قبل وسم الإصدار. ## 2. Product and legal surface diff --git a/docs/executive-room-completion-spec.md b/docs/executive-room-completion-spec.md index b1f57964..77c3864e 100644 --- a/docs/executive-room-completion-spec.md +++ b/docs/executive-room-completion-spec.md @@ -2,6 +2,11 @@ **Goal:** Executive-visible surfaces backed by **trusted data only** (no hallucinated KPIs). +## Structured weekly contract (WS8 — single UI/API shape) + +**Canonical schema:** [`ExecWeeklyGovernanceContract`](../salesflow-saas/backend/app/schemas/structured_outputs.py) — حقول `changes_summary`, `pending_decisions`, `blockers_summary`, `at_risk_items`, `next_best_actions`, `week_of`, `provenance`. +**Legacy / PMI richness:** [`ExecWeeklyPack`](../salesflow-saas/backend/app/schemas/structured_outputs.py) (RAG، synergy SAR، إلخ) يبقى للتقارير المالية؛ الواجهات التنفيذية الجديدة تفضّل `ExecWeeklyGovernanceContract`. + ## Milestones 0. **Class B bundle API (pilot)** — `GET /api/v1/approval-center/class-b-decision-bundle` returns a validated bundle (`validate_class_b_bundle`); frontend can bind read-only viewers to this shape before DB-backed queues exist. diff --git a/docs/github-enterprise-delivery-completion.md b/docs/github-enterprise-delivery-completion.md index d40a54e3..ab74f215 100644 --- a/docs/github-enterprise-delivery-completion.md +++ b/docs/github-enterprise-delivery-completion.md @@ -27,3 +27,7 @@ ## Evidence Store screenshots or org policy links (internal) as evidence for enterprise questionnaires; do not commit secrets. + +## Observability (OTel-style correlation) + +Deploy and approval workflows SHOULD propagate **`trace_id` / `span_id` / `correlation_id`** into internal audit exports so GitHub Actions events can be joined with application logs — aligned with [`governance/trust-fabric.md`](governance/trust-fabric.md) runtime policies. diff --git a/docs/governance/approval-policy.md b/docs/governance/approval-policy.md index 3fac868f..53d38e2f 100644 --- a/docs/governance/approval-policy.md +++ b/docs/governance/approval-policy.md @@ -83,6 +83,24 @@ For Class B and any R2/R3 decision: sources, assumptions, timestamps/freshness, A **decision memo without an evidence pack is incomplete.** +## Class B decision bundle gate (P0 — Tier-1 enterprise) + +Any **Class B or higher** response that represents a governed decision MUST expose the unified bundle (see [`decision_plane_contracts.py`](../../salesflow-saas/backend/app/services/core_os/decision_plane_contracts.py)) with at minimum: + +| Key | Role | +|-----|------| +| `memo_json` | `DecisionMemo` including non-empty `required_approvals` | +| `evidence_pack_json` | Structured evidence | +| `risk_register_json` | List (may be empty only if explicitly allowed by policy) | +| `approval_packet_json` | A/R/S + actor | +| `execution_intent_json` | Workflow key + idempotency + side-effect class | + +**Correlation / trace (P0):** For `requested_side_effect_class` of `external_message` or `external_commitment`, `execution_intent_json.correlation_id` MUST be non-empty (enforced by `validate_class_b_bundle`). Prefer propagating the same value into `audit_metadata.trace_id` on the memo when OpenTelemetry is enabled. See [`trust-fabric.md`](trust-fabric.md) observability section. + +## Operational severity (V0–V3) + +Policy violations, contradictions, connector failures, and workflow failures SHOULD be classified with one scale for dashboards and release gates — see [`operational-severity-model.md`](operational-severity-model.md) and [`../RELEASE_READINESS_MATRIX_AR.md`](../RELEASE_READINESS_MATRIX_AR.md). + ## GitHub governance (surface) See [github-and-release.md](github-and-release.md) for the full model. Summary: protected `main`, required reviews and checks, CODEOWNERS as team scales, secret scanning and dependency review, OIDC for deploy keys where possible, environment promotion (dev → staging → canary → prod). diff --git a/docs/governance/execution-fabric.md b/docs/governance/execution-fabric.md index 8497a249..bfa0a671 100644 --- a/docs/governance/execution-fabric.md +++ b/docs/governance/execution-fabric.md @@ -60,3 +60,23 @@ Any new execution path that sends customer messages, moves money, signs contract 3. Pass **security gate** and release checklist for the environment. See also: [events-and-schema.md](events-and-schema.md), [trust-fabric.md](trust-fabric.md), [github-and-release.md](github-and-release.md). + +--- + +## LangGraph durability modes (policy sketch) + +Classify each graph by **how state must survive** process restarts and deploys: + +| Mode | When to use | Notes | +|------|-------------|--------| +| **`exit`** | Ephemeral assistance, no business state | Graph ends with the HTTP/session; no recovery requirement. | +| **`async`** | Bounded background continuation acceptable | Tasks may be lost on crash unless explicitly checkpointed — document the loss window. | +| **`sync` / durable checkpoint** | HITL waits, multi-step approvals, or any path that can cause **external_message** / **external_commitment** | Require checkpointing + idempotency keys aligned with `ExecutionIntent`; prefer graduating external effects to Temporal per division-of-labor above. | + +External references: LangGraph durable execution — [`../references/tier1-external-index.md`](../references/tier1-external-index.md). + +## HITL taxonomy (approve / edit / reject) + +Human-in-the-loop steps on governed paths MUST record one of: **`approve`** (proceed as proposed), **`edit`** (proceed with amended structured payload), **`reject`** (terminate with reason). Map API fields and audit events to this taxonomy consistently (LangChain HITL vocabulary — same external index). + +**Rule:** `reject` on Class B / R2+ MUST emit a policy-safe audit row and MUST NOT leave dangling `ExecutionIntent` rows marked runnable. diff --git a/docs/governance/operational-severity-model.md b/docs/governance/operational-severity-model.md new file mode 100644 index 00000000..8456c59a --- /dev/null +++ b/docs/governance/operational-severity-model.md @@ -0,0 +1,18 @@ +# Operational severity model (V0–V3) + +**Canonical:** [`MASTER_OPERATING_PROMPT.md`](../../MASTER_OPERATING_PROMPT.md). +**Related:** [approval-policy.md](approval-policy.md) (A/R/S), [`RELEASE_READINESS_MATRIX_AR.md`](../RELEASE_READINESS_MATRIX_AR.md). + +Use this scale for **policy violations**, **ledger contradictions**, **connector health**, **workflow failures**, and **release gate** items so dashboards and runbooks speak one language. + +| Level | Name (EN) | تعريف مختصر (AR) | أمثلة | تأثير على الإصدار | +|-------|-----------|------------------|--------|---------------------| +| **V0** | Informational | ملاحظة تشغيلية بلا تأثير مباشر على العميل | تحذيرات deprecated، drift توثيقي | لا يعطل RC | +| **V1** | Operational | يتطلب إجراءًا داخليًا في SLA قصير | فشل مهمة خلفية مع إعادة محاولة، انحراف مقياس داخلي | لا يعطل RC إن وُجدت آلية تعويض | +| **V2** | Customer-impacting | يؤثر على تجربة عميل أو بيانات حساسة | فشل موصل حرج، تأخير موافقة Class B، تعارض أدلة جزئي | **يمنع** ترقية canary→prod حتى التخفيف أو الاستثناء المسجل | +| **V3** | Regulatory / release-blocking | يعطل الإصدار أو يرفع مخاطر امتثال | تسريب محتمل، تعارض أدلة على مسار R2/R3، غياب بوابة أمان | **Stop ship**؛ راجع [`trust-fabric.md`](trust-fabric.md) و[`github-and-release.md`](github-and-release.md) | + +## Wiring (target) + +- اربط كل حدث حوكمة بـ `severity` (V0–V3) في السجلات واللوحات. +- صفوف [`RELEASE_READINESS_MATRIX_AR.md`](../RELEASE_READINESS_MATRIX_AR.md) تلخص أعلى خطورة مفتوحة لكل مرشح إصدار. diff --git a/docs/governance/pdpl-nca-ai-control-matrices.md b/docs/governance/pdpl-nca-ai-control-matrices.md index 6049bad5..defa63a9 100644 --- a/docs/governance/pdpl-nca-ai-control-matrices.md +++ b/docs/governance/pdpl-nca-ai-control-matrices.md @@ -35,6 +35,23 @@ | Insecure output handling | Schema validation on outputs | pytest | | Excessive agency | Executor vs recommender separation | Policy review | +### NIST AI RMF GenAI profile + OWASP LLM Top 10 (2025) → Dealix planes + +Map each control or test case to **one primary plane** so WS owners stay accountable (see [`../SOURCE_OF_TRUTH_INDEX.md`](../SOURCE_OF_TRUTH_INDEX.md) for doc hierarchy). + +| مصدر / مخاطرة | طائرة القرار (Decision) | طائرة الثقة (Trust) | الموصلات (Connector) | البيانات (Data) | التشغيل / التكلفة (Runtime) | +|----------------|-------------------------|----------------------|----------------------|-----------------|-------------------------------| +| GenAI profile — Govern | سياسات memos، تصنيف A/R/S | سياسات تدقيق وسجلات | — | سياسات الاحتفاظ | حدود ميزانية نماذج | +| GenAI profile — Map | تدفق prompt/PII في القرار | تدفق أدوات وMCP | حدود موصل الطرف الثالث | مصادر بيانات حساسة | تتبع تكلفة tokens | +| GenAI profile — Measure | تقييم structured outputs | red-team، contradictions | اختبارات فشل موصل | GE checkpoints | SLO زمن استجابة | +| GenAI profile — Manage | HITL، رفض مسار | حوادث، rollback | تعطيل موصل | إيقاف تزامن بيانات | إصدار نماذج | +| OWASP LLM — Prompt injection | وكلاء + prompts | tool verification | واجهات runtime tools | — | — | +| OWASP LLM — Insecure output handling | Pydantic / JSON schema | ledger على المطالبات | — | — | — | +| OWASP LLM — Excessive agency | Decision vs Execution فصل | ApprovalPacket | — | — | workflow gates | +| OWASP LLM — Sensitive disclosure | تصنيف S2/S3 | تدقيق تصدير | scopes موصل | PDPL minimization | تسجيل مراقبة | + +**مراجع خارجية:** [`../references/tier1-external-index.md`](../references/tier1-external-index.md). + ## Region / residency flags Define configuration keys for **data region** and **LLM routing** per tenant; document in ADR when enforced in `policy_engine` or external PDP. @@ -46,5 +63,6 @@ Define configuration keys for **data region** and **LLM routing** per tenant; do Before tagging an **enterprise** release candidate: 1. Reconcile this matrix with [`../enterprise-readiness.md`](../enterprise-readiness.md) and [`saudi-compliance-and-ai-governance.md`](saudi-compliance-and-ai-governance.md). -2. Attach evidence: PDPL rows above filled (no `…` placeholders for production claims), NCA gap register owner + date, AI RMF row sign-off. -3. Cross-check [`../TIER1_MASTER_CLOSURE_CHECKLIST_AR.md`](../TIER1_MASTER_CLOSURE_CHECKLIST_AR.md) §14 and [`../../salesflow-saas/docs/tier1-master-closure-checklist.md`](../../salesflow-saas/docs/tier1-master-closure-checklist.md) Gate 8. +2. Fill a row in [`../RELEASE_READINESS_MATRIX_AR.md`](../RELEASE_READINESS_MATRIX_AR.md) for the RC (docs truth, connectors, security, Saudi, provenance). +3. Attach evidence: PDPL rows above filled (no `…` placeholders for production claims), NCA gap register owner + date, AI RMF row sign-off. +4. Cross-check [`../TIER1_MASTER_CLOSURE_CHECKLIST_AR.md`](../TIER1_MASTER_CLOSURE_CHECKLIST_AR.md) §14 and [`../../salesflow-saas/docs/tier1-master-closure-checklist.md`](../../salesflow-saas/docs/tier1-master-closure-checklist.md) Gate 8. diff --git a/docs/governance/saudi-compliance-and-ai-governance.md b/docs/governance/saudi-compliance-and-ai-governance.md index 6142fbc4..871a3555 100644 --- a/docs/governance/saudi-compliance-and-ai-governance.md +++ b/docs/governance/saudi-compliance-and-ai-governance.md @@ -43,6 +43,18 @@ Use as a **risk and testing** frame for agentic features: Pair with [trust-fabric.md](trust-fabric.md): red-team workflows, structured output validation, tool allowlists, and rollback plans for Class B / R2+. +### Plane overlay (where each frame lands) + +| Plane | NIST GenAI emphasis | OWASP LLM emphasis | +|-------|---------------------|-------------------| +| **Decision** | Map/manage model behavior in prompts & memos | Prompt injection, insecure output handling | +| **Trust** | Measure/manage evaluations & incidents | Sensitive disclosure, excessive agency (governance) | +| **Connector** | Map third-party tool/data exposure | Supply chain for tools/MCP | +| **Data** | Map PDPL-relevant flows | Training data poisoning (if applicable) | +| **Runtime / cost** | Manage capacity & monitoring | Unbounded tool loops, denial of wallet | + +Detailed control rows live in [pdpl-nca-ai-control-matrices.md](pdpl-nca-ai-control-matrices.md). External link list: [`../references/tier1-external-index.md`](../references/tier1-external-index.md). + **References (external):** NIST publications portal; OWASP LLM Top 10 and GenAI security project pages. --- diff --git a/docs/governance/trust-fabric.md b/docs/governance/trust-fabric.md index 7f78edc6..b96e7974 100644 --- a/docs/governance/trust-fabric.md +++ b/docs/governance/trust-fabric.md @@ -71,3 +71,22 @@ The following are **architecture targets** for enterprise-grade trust. They are **Integration pattern:** policy engines and PDPs should consume the same **A/R/S** and **actor_type** fields as events (see [events-and-schema.md](events-and-schema.md)) — avoid duplicating conflicting rules in prompts. **Spike gate:** no production dependency on OPA/OpenFGA/Vault/Keycloak until ADR + security review + tests; see [`../adr/0001-tier1-execution-policy-spikes.md`](../adr/0001-tier1-execution-policy-spikes.md). + +--- + +## Runtime policies (Tier-1 operational — beyond the radar) + +These are **enforcement expectations** once a component is in-path in production (pair with [`github-and-release.md`](github-and-release.md) and [`operational-severity-model.md`](operational-severity-model.md)). + +### OpenFGA — pinned authorization models + +- **No production call path** without a recorded **`authorization_model_id`** (or equivalent immutable model version) in configuration and deploy manifests. Models are **immutable** in OpenFGA; pin IDs per environment and rotate via controlled rollout — see [`../references/tier1-external-index.md`](../references/tier1-external-index.md) (OpenFGA links). +- Agent-on-behalf-of-user flows MUST be modeled explicitly (no implicit super-user tuples). + +### Vault (or equivalent) — dual audit devices + +- Production clusters MUST enable **at least two** independent audit devices (e.g. file + SIEM socket) so tampering or loss of one sink does not erase the audit trail — see Vault audit documentation in [`../references/tier1-external-index.md`](../references/tier1-external-index.md). + +### OpenTelemetry — log correlation + +- Critical paths (approvals, external commitments, connector facade calls) MUST emit **`trace_id`**, **`span_id`**, and a stable **`correlation_id`** (or equivalent) in structured logs and audit receipts so SIEM queries can join API ↔ worker ↔ workflow — see OTel logging spec in [`../references/tier1-external-index.md`](../references/tier1-external-index.md). diff --git a/docs/references/tier1-external-index.md b/docs/references/tier1-external-index.md new file mode 100644 index 00000000..359dd22b --- /dev/null +++ b/docs/references/tier1-external-index.md @@ -0,0 +1,23 @@ +# Tier-1 external references (appendix) + +روابط خارجية تُستخدم في الحوكمة والخطط؛ **لا تُكرر النص الطويل** في الوثائق الرئيسية—أشر إلى هذا الملف. + +| الموضوع | مرجع | +|---------|--------| +| OpenAI Structured Outputs | https://openai.com/index/introducing-structured-outputs-in-the-api/ | +| OpenAI Responses API | https://platform.openai.com/docs/api-reference/responses | +| LangGraph durable execution | https://docs.langchain.com/oss/javascript/langgraph/durable-execution | +| LangGraph / LangChain HITL | https://docs.langchain.com/oss/javascript/langchain/frontend/human-in-the-loop | +| Temporal worker versioning | https://temporal.io/change-log/worker-versioning-public-preview | +| OpenFGA immutable models | https://openfga.dev/docs/getting-started/immutable-models | +| OpenFGA authorization for agents | https://openfga.dev/docs/modeling/agents | +| HashiCorp Vault audit devices | https://developer.hashicorp.com/vault/docs/audit | +| OPA | https://www.openpolicyagent.org/docs | +| Keycloak Server Admin | https://www.keycloak.org/docs/latest/server_admin/index.html | +| OpenTelemetry Logging | https://opentelemetry.io/docs/specification/logs/ | +| CloudEvents spec | https://github.com/cloudevents/spec | +| NIST AI RMF GenAI profile | https://www.nist.gov/publications/artificial-intelligence-risk-management-framework-generative-artificial-intelligence | +| OWASP LLM Top 10 | https://owasp.org/www-project-top-10-for-large-language-model-applications/ | +| Great Expectations checkpoints | https://docs.greatexpectations.io/docs/0.18/reference/learn/terms/checkpoint | +| Airbyte docs | https://docs.airbyte.com/ | +| Airbyte MCP / agentic data | https://airbyte.com/agentic-data/mcp-connectors | diff --git a/docs/ws5-connector-events-metrics.md b/docs/ws5-connector-events-metrics.md index 17fd89d7..bc61629e 100644 --- a/docs/ws5-connector-events-metrics.md +++ b/docs/ws5-connector-events-metrics.md @@ -19,3 +19,29 @@ Authoritative definitions: [`semantic-metrics-dictionary.md`](semantic-metrics-d ## Lineage / catalog Single choice documented in [`lineage-catalog-choice.md`](lineage-catalog-choice.md) until an ADR changes it. + +--- + +## Dual connector strategy (runtime tools vs data movement) + +| Layer | Purpose | Examples | Governance | +|-------|---------|-----------|------------| +| **Runtime tool connectors** | LLM/agent tool calls during a session (read/write bounded) | MCP tools, built-in HTTP tools behind facade | Decision plane + tool verification + allowlists | +| **Data movement connectors** | Scheduled/batch replication or sync between systems | ELT/CDC-style pipelines (Airbyte-class) | Execution plane ownership, SLAs, backfill runbooks | + +**Rule:** never confuse the two in architecture diagrams — they share “connector” language but have different failure domains, retention, and approval classes. + +### MCP Tasks / async operations (watchlist) + +Long-running MCP operations (tasks that outlive a single HTTP interaction) MUST have: visible **status polling** or webhook, **timeout**, **idempotency key**, and an owner in [`semantic-metrics-dictionary.md`](semantic-metrics-dictionary.md) or the connector runbook. Track vendor-specific “MCP Tasks” behavior as **V1–V2** severity candidates when SLAs slip — see [`governance/operational-severity-model.md`](governance/operational-severity-model.md). External overview: [`references/tier1-external-index.md`](references/tier1-external-index.md) (Airbyte MCP). + +--- + +## Great Expectations (GE) as a release gate + +When GE is in-path: + +- Tie **checkpoint success** explicitly to [`RELEASE_READINESS_MATRIX_AR.md`](RELEASE_READINESS_MATRIX_AR.md) (row: schema adherence / data quality) and to promotion of workflows that consume the underlying datasets. +- Failed checkpoints are **V2** by default if customer reports or downstream models consume the data; **V3** if PII/regulated fields are involved. + +Reference: GE checkpoints — [`references/tier1-external-index.md`](references/tier1-external-index.md). diff --git a/salesflow-saas/backend/app/api/v1/approval_center.py b/salesflow-saas/backend/app/api/v1/approval_center.py index f1a78f3c..57f69de6 100644 --- a/salesflow-saas/backend/app/api/v1/approval_center.py +++ b/salesflow-saas/backend/app/api/v1/approval_center.py @@ -69,9 +69,9 @@ async def class_b_decision_bundle_demo() -> Dict[str, Any]: execution_intent = ExecutionIntent( workflow_key="governance_class_b_review_v1", idempotency_key="class-b-demo-approval-center-001", - requested_side_effect_class="internal_write", + requested_side_effect_class="external_message", correlation_id="corr_class_b_demo", - payload_summary="Record approval decision in internal audit trail", + payload_summary="Demo external-class path; correlation_id mandatory per validate_class_b_bundle", ) bundle = assemble_decision_bundle( evidence_pack=evidence, diff --git a/salesflow-saas/backend/app/schemas/structured_outputs.py b/salesflow-saas/backend/app/schemas/structured_outputs.py index 3e7797af..4a8e79f0 100644 --- a/salesflow-saas/backend/app/schemas/structured_outputs.py +++ b/salesflow-saas/backend/app/schemas/structured_outputs.py @@ -269,3 +269,25 @@ class ExecWeeklyPack(BaseModel): people_update: str risk_summary: List[str] provenance: Provenance + + +class ExecWeeklyGovernanceContract(BaseModel): + """Unified executive weekly contract (WS8) — structured fields for UI/API. + + Use alongside :class:`ExecWeeklyPack` for legacy synergy/RAG rows; prefer this + model for new executive surfaces and approval-room summaries. + """ + + week_of: str = Field(description="ISO week label, e.g. 2026-W16") + changes_summary: str = Field(description="What moved this week (facts + links)") + pending_decisions: List[str] = Field( + default_factory=list, description="Items awaiting human decision" + ) + blockers_summary: str = Field(description="Single narrative of blockers") + at_risk_items: List[str] = Field( + default_factory=list, description="Initiatives or gates at risk" + ) + next_best_actions: List[str] = Field( + default_factory=list, description="Prioritized next actions with owners if known" + ) + provenance: Provenance diff --git a/salesflow-saas/backend/app/services/core_os/decision_memo.py b/salesflow-saas/backend/app/services/core_os/decision_memo.py index 51babc79..8d99bf4a 100644 --- a/salesflow-saas/backend/app/services/core_os/decision_memo.py +++ b/salesflow-saas/backend/app/services/core_os/decision_memo.py @@ -19,6 +19,11 @@ class AuditMetadata(BaseModel): policy_check_passed: bool = False agent_id: str timestamp: str + trace_id: Optional[str] = Field( + default=None, + description="Distributed trace id (align with OpenTelemetry trace_id when OTel is enabled)", + ) + span_id: Optional[str] = Field(default=None, description="Optional OTel span id for log correlation") class DecisionMemo(BaseModel): """ diff --git a/salesflow-saas/backend/app/services/core_os/decision_plane_contracts.py b/salesflow-saas/backend/app/services/core_os/decision_plane_contracts.py index bd945111..b4adf320 100644 --- a/salesflow-saas/backend/app/services/core_os/decision_plane_contracts.py +++ b/salesflow-saas/backend/app/services/core_os/decision_plane_contracts.py @@ -86,7 +86,14 @@ def validate_class_b_bundle(bundle: Dict[str, Any]) -> None: raise ValueError("memo_json.required_approvals must be non-empty for Class B paths") EvidencePack.model_validate(bundle["evidence_pack_json"]) ApprovalPacket.model_validate(bundle["approval_packet_json"]) - ExecutionIntent.model_validate(bundle["execution_intent_json"]) + ei = ExecutionIntent.model_validate(bundle["execution_intent_json"]) + if ei.requested_side_effect_class in ("external_message", "external_commitment"): + cid = (ei.correlation_id or "").strip() + if not cid: + raise ValueError( + "execution_intent_json.correlation_id is required for " + f"requested_side_effect_class={ei.requested_side_effect_class!r} (traceability / OTel)" + ) rr = bundle["risk_register_json"] if not isinstance(rr, list): raise ValueError("risk_register_json must be a list") diff --git a/salesflow-saas/backend/tests/test_decision_plane_contracts.py b/salesflow-saas/backend/tests/test_decision_plane_contracts.py index 7f4004b4..0cb8523d 100644 --- a/salesflow-saas/backend/tests/test_decision_plane_contracts.py +++ b/salesflow-saas/backend/tests/test_decision_plane_contracts.py @@ -67,6 +67,48 @@ def test_assemble_decision_bundle_keys(): validate_class_b_bundle(bundle) +def test_validate_class_b_bundle_requires_correlation_for_external_commitment(): + memo = DecisionMemo( + objective="x", + decision_context="c", + inputs_used=["i"], + assumptions=["a"], + recommendation_ar="r", + alternatives_considered=["b"], + expected_financial_impact=FinancialImpact(), + risk_register=[], + confidence_score=50.0, + required_approvals=["CFO"], + next_best_action="n", + rollback_plan="rb", + evidence_links=[], + audit_metadata=AuditMetadata(agent_id="a", timestamp="2026-01-01T00:00:00Z"), + ) + ep = EvidencePack(pack_id=new_evidence_pack_id(), provenance_score=1.0) + ap = ApprovalPacket( + approval_class="A2", + reversibility_class="R2", + sensitivity_class="S2", + actor_type="executor_system", + approvers_required=["CFO"], + ) + ei = ExecutionIntent( + workflow_key="w", + idempotency_key="k", + requested_side_effect_class="external_commitment", + correlation_id=" ", + payload_summary="x", + ) + bundle = assemble_decision_bundle( + evidence_pack=ep, + approval_packet=ap, + execution_intent=ei, + memo_json=memo.to_json(), + ) + with pytest.raises(ValueError, match="correlation_id"): + validate_class_b_bundle(bundle) + + def test_validate_class_b_bundle_rejects_missing_memo_approvals(): memo = DecisionMemo( objective="x", diff --git a/scripts/architecture_brief.py b/scripts/architecture_brief.py index b10fd1a0..479a3e2f 100644 --- a/scripts/architecture_brief.py +++ b/scripts/architecture_brief.py @@ -18,6 +18,13 @@ CONSTITUTION_PATHS = [ "docs/architecture-register.md", "docs/execution-matrix-90d-tier1.md", "docs/enterprise-readiness.md", + "docs/RELEASE_READINESS_MATRIX_AR.md", + "docs/SOURCE_OF_TRUTH_INDEX.md", + "docs/references/tier1-external-index.md", + "docs/ws5-connector-events-metrics.md", + "docs/executive-room-completion-spec.md", + "docs/governance/pdpl-nca-ai-control-matrices.md", + "docs/governance/operational-severity-model.md", "docs/adr/0001-tier1-execution-policy-spikes.md", "docs/adr/0002-execution-matrix-canonical-source.md", "docs/TIER1_MASTER_CLOSURE_CHECKLIST_AR.md", diff --git a/scripts/check_docs_links.py b/scripts/check_docs_links.py new file mode 100644 index 00000000..54a7f29d --- /dev/null +++ b/scripts/check_docs_links.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +"""Verify relative markdown links from repo root (Tier-1 docs governance CI).""" +from __future__ import annotations + +import re +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +LINK_RE = re.compile(r"\[[^\]]*\]\(([^)]+)\)") + +# Markdown files to scan (repo-grounded governance spine) +GLOBS = [ + "docs/**/*.md", + "MASTER_OPERATING_PROMPT.md", + "AGENTS.md", + "CLAUDE.md", + "Execution_Matrix.md", + "Execution_Matrix_v2.md", + "Architecture_Pack.md", +] + + +def collect_files() -> list[Path]: + out: list[Path] = [] + for g in GLOBS: + if "**" in g: + out.extend(p for p in ROOT.glob(g) if p.is_file()) + else: + p = ROOT / g + if p.is_file(): + out.append(p) + return sorted(set(out)) + + +def resolve_link(source: Path, raw_target: str) -> Path | None: + target = raw_target.split("#", 1)[0].strip() + if not target or target.startswith(("http://", "https://", "mailto:", "vscode:", "file:")): + return None + if target.startswith("/"): + return (ROOT / target.lstrip("/")).resolve() + base = source.parent + return (base / target).resolve() + + +def main() -> int: + errors: list[str] = [] + for md in collect_files(): + try: + text = md.read_text(encoding="utf-8") + except OSError as e: + errors.append(f"{md.relative_to(ROOT)}: read error {e}") + continue + for m in LINK_RE.finditer(text): + raw = m.group(1).strip() + if "`" in raw or raw.startswith("{{"): + continue + resolved = resolve_link(md, raw) + if resolved is None: + continue + try: + resolved.relative_to(ROOT) + except ValueError: + errors.append(f"{md.relative_to(ROOT)}: escape path {raw}") + continue + if not resolved.exists(): + errors.append(f"{md.relative_to(ROOT)}: broken link -> {raw} (resolved {resolved.relative_to(ROOT)})") + + if errors: + print("Docs link check FAILED:\n", file=sys.stderr) + for e in errors[:80]: + print(f" {e}", file=sys.stderr) + if len(errors) > 80: + print(f" ... and {len(errors) - 80} more", file=sys.stderr) + return 1 + print(f"Docs link check OK ({len(collect_files())} files scanned)") + return 0 + + +if __name__ == "__main__": + sys.exit(main())