From 70290711a5ee00fffe1bff4ece3565872e464852 Mon Sep 17 00:00:00 2001 From: Estandar <143508382+EstandarMustaq@users.noreply.github.com> Date: Fri, 17 Jul 2026 00:17:16 +0200 Subject: [PATCH 1/3] feat: enforce cloud banking agent skills --- .agents/AGENTS.md | 11 +- .agents/skills/mavula-cloud-banking/SKILL.md | 62 ++++++++++ .../mavula-cloud-banking/agents/openai.yaml | 4 + .../references/banking-operations.md | 59 ++++++++++ .../references/cloud-native-scale.md | 58 ++++++++++ .../references/composability-no-code.md | 46 ++++++++ .../references/engineering-data.md | 57 ++++++++++ .../references/module-ownership.md | 29 +++++ .../references/security-regulation.md | 53 +++++++++ .agents/skills/mavula-review/SKILL.md | 107 +++++++++--------- .../skills/mavula-review/agents/openai.yaml | 4 +- .cursor/rules/mavula-engineering.mdc | 12 ++ .github/copilot-instructions.md | 9 ++ .gitignore | 14 +++ scripts/check-agent-policy.mjs | 61 ++++++++++ scripts/guardian.mjs | 2 + 16 files changed, 530 insertions(+), 58 deletions(-) create mode 100644 .agents/skills/mavula-cloud-banking/SKILL.md create mode 100644 .agents/skills/mavula-cloud-banking/agents/openai.yaml create mode 100644 .agents/skills/mavula-cloud-banking/references/banking-operations.md create mode 100644 .agents/skills/mavula-cloud-banking/references/cloud-native-scale.md create mode 100644 .agents/skills/mavula-cloud-banking/references/composability-no-code.md create mode 100644 .agents/skills/mavula-cloud-banking/references/engineering-data.md create mode 100644 .agents/skills/mavula-cloud-banking/references/module-ownership.md create mode 100644 .agents/skills/mavula-cloud-banking/references/security-regulation.md create mode 100644 .cursor/rules/mavula-engineering.mdc create mode 100644 .github/copilot-instructions.md create mode 100644 scripts/check-agent-policy.mjs diff --git a/.agents/AGENTS.md b/.agents/AGENTS.md index b621d89..01b849f 100644 --- a/.agents/AGENTS.md +++ b/.agents/AGENTS.md @@ -1,5 +1,12 @@ # MAVULA Agent Entry Point -Use `.agents/skills/mavula-review/SKILL.md` for MAVULA code reviews, security reviews, database reviews, and cross-repository implementation checks. +Use `.agents/skills/mavula-cloud-banking/SKILL.md` for architecture, implementation, +production readiness, security, operations, scalability, and configurable banking +workflows. -Keep this directory focused on reusable agent instructions. Do not add local transcripts, prompts, generated reports, credentials, or temporary agent state. +Use `.agents/skills/mavula-review/SKILL.md` for pull request, security, database, +migration, contract, and production-readiness reviews. Production-impact reviews +must also load the relevant `mavula-cloud-banking` reference. + +Keep `.agents` limited to canonical reusable instructions. Never add transcripts, +local prompts, generated reports, credentials, or temporary agent state. diff --git a/.agents/skills/mavula-cloud-banking/SKILL.md b/.agents/skills/mavula-cloud-banking/SKILL.md new file mode 100644 index 0000000..444c3fb --- /dev/null +++ b/.agents/skills/mavula-cloud-banking/SKILL.md @@ -0,0 +1,62 @@ +--- +name: mavula-cloud-banking +description: Design, implement, operate, and assess MAVULA as regulated cloud-native banking infrastructure. Use for real institutional workflows, module boundaries, financial operations, identity, payments, compliance, configurable and no-code products, composable APIs and events, production security, Kubernetes and AWS operations, resilience, observability, capacity, and high-scale readiness across all MAVULA repositories. +--- + +# MAVULA Cloud Banking + +Use this skill for engineering work that can affect institutional access, +financial state, payment state, regulatory evidence, runtime reliability, or +platform configuration. Ground every decision in repository contracts and +observable failure behavior. + +## Workflow + +1. Read the context map, invariants, touched contracts, migrations, runtime code, + tests, and deployment configuration. Distinguish implemented behavior from + roadmap intent. +2. Name the owner, caller, data classification, tenant boundary, financial + effect, consistency requirement, idempotency identity, audit evidence, and + recovery path. +3. Trace the full operation across synchronous APIs, owner transaction, + Outbox/Inbox, Workbench jobs, projections, monitoring, and operator action. +4. Define compatibility and failure behavior before implementation: timeout, + duplicate, reordering, partial failure, retry exhaustion, replay, rollback, + and reconciliation. +5. Quantify production assumptions per service: SLO, workload, peak concurrency, + data growth, RTO/RPO, failure domains, and capacity-test evidence. +6. Implement within the owner boundary using the repository's established + language, database, contract, migration, and guardian patterns. +7. Verify invariant tests, contract tests, security boundaries, failure paths, + observability, operational recovery, and CI-equivalent checks. + +## Hard Rules + +- Identity context is signed and authoritative. Payload identity never grants + tenant, institution, branch, role, or permission. +- `ledger-core` alone owns financial invariants and journal posting. Posted + records are immutable; correction uses controlled reversal or adjustment. +- `settlements` owns external payment state. A settlement event does not mutate + ledger or lending directly; an authorized ledger command is required. +- `workbench` owns operational jobs, not business truth. Retry is bounded and + every side effect is idempotent and auditable. +- PostgreSQL and owner aggregates are authoritative. Redis is transport and + temporary operational state. +- Public APIs and events are versioned contracts. Consumers never depend on + producer tables, private DTOs, or deployment internals. +- Configuration is declarative, tenant-scoped, versioned, validated, bounded, + approved where required, and immutable after publication. +- Production claims require measured evidence. Regulatory and certification + claims require explicit scope and accountable approval. + +## References + +- Module authority and allowed integration: `references/module-ownership.md`. +- Real institutional workflows and failure paths: `references/banking-operations.md`. +- Security, data protection, and regulatory baseline: `references/security-regulation.md`. +- Kubernetes, AWS, resilience, observability, and capacity: `references/cloud-native-scale.md`. +- API composability and governed no-code configuration: `references/composability-no-code.md`. +- Language, PostgreSQL, Redis, and COBOL rules: `references/engineering-data.md`. + +Load only the references relevant to the task. For reviews, use the companion +`mavula-review` skill so findings retain MAVULA severity and evidence format. diff --git a/.agents/skills/mavula-cloud-banking/agents/openai.yaml b/.agents/skills/mavula-cloud-banking/agents/openai.yaml new file mode 100644 index 0000000..bd7e02d --- /dev/null +++ b/.agents/skills/mavula-cloud-banking/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "MAVULA Cloud Banking" + short_description: "Engineer regulated, composable cloud-banking systems" + default_prompt: "Use $mavula-cloud-banking to design or implement this MAVULA change with explicit ownership, financial invariants, security, recovery, operability, and scale evidence." diff --git a/.agents/skills/mavula-cloud-banking/references/banking-operations.md b/.agents/skills/mavula-cloud-banking/references/banking-operations.md new file mode 100644 index 0000000..33dcc9d --- /dev/null +++ b/.agents/skills/mavula-cloud-banking/references/banking-operations.md @@ -0,0 +1,59 @@ +# Banking Operations + +## Institutional Access + +1. `identity-access` authenticates the operator or workload and derives effective + institution, branch, tenant, roles, and permissions from persisted state. +2. Resource servers validate issuer, audience, signature, expiry, token purpose, + tenant binding, and operation permission before reading request data. +3. Sensitive actions record actor, effective role, source, correlation, result, + and maker-checker evidence without placing credentials or unnecessary PII in events. + +## Product And Workflow Configuration + +1. An authorized configuration operator submits a schema-valid tenant-scoped draft. +2. The owner validates references, deterministic rules, limits, effective dates, + and approval policy before activation. +3. Publication creates an immutable version and audit record. Runtime operations + retain the exact product, rule, schema, and workflow versions used. +4. Rollback activates a known compatible version; it never mutates history. + +## Accounts, Ledger, And Lending + +1. Every write requires authenticated tenant context, correlation, and durable + idempotency when it can create a financial or external side effect. +2. The owner validates account/product state, currency, precision, limits, + maker-checker, and later-effect constraints inside one transaction. +3. Journal lines balance by currency. Subledger, audit, receipt, and Outbox are + committed atomically with the owner state. +4. Freeze, unfreeze, close, reversal, and correction preserve immutable history; + self-approval and duplicate approval are rejected. + +## Payments And Reconciliation + +1. Workbench starts an authorized payment job with tenant, correlation, amount, + rail, parties, and idempotency identity. +2. Settlements persists the process before external interaction and authenticates + callbacks before durable dedupe and explicit state transition. +3. Reconciliation compares provider, process, and expected ledger references. + Mismatch remains actionable and cannot be hidden by job success. +4. Settlement completion is recorded in Outbox and published by Workbench. + Ledger Core records Inbox idempotently and performs no direct financial + mutation from the event. + +## Regulatory And Legacy Processing + +1. An authorized compliance operator requests an export with period, legal basis, + retention, tenant, and idempotency context. +2. Ledger Core provides the approved source contract; Legacy Connectors generates + deterministic fixed-width content, totals, checksum, receipt, and artifact. +3. Workbench controls leases, bounded retries, DLQ, and delivery recording. + Imports remain staging and validation only unless a future owner command is approved. + +## Incident And Recovery + +- Diagnose owner state separately from queue state. A completed job does not prove + a completed business operation. +- Replay requires authorization, reason, original correlation, dedupe, and audit. +- Recovery verifies database consistency, Outbox/Inbox backlog, queue lag, DLQ, + settlement reconciliation, projection freshness, and regulatory artifact integrity. diff --git a/.agents/skills/mavula-cloud-banking/references/cloud-native-scale.md b/.agents/skills/mavula-cloud-banking/references/cloud-native-scale.md new file mode 100644 index 0000000..d3066a1 --- /dev/null +++ b/.agents/skills/mavula-cloud-banking/references/cloud-native-scale.md @@ -0,0 +1,58 @@ +# Cloud-Native Scale + +## Service Readiness Budget + +Every production service or material change must document: + +- availability and latency SLOs with measured indicators and error-budget owner; +- normal, peak, burst, and degraded workload plus concurrency and tenant skew; +- data volume, growth, retention, hot-key or hot-tenant risk, and capacity horizon; +- RTO, RPO, backup frequency, restore procedure, and last recovery-test evidence; +- regional and zonal failure domains, dependency assumptions, and degradation mode; +- load, soak, fault, retry-storm, and recovery evidence at the claimed envelope. + +Do not invent one platform-wide number. Missing budgets invalidate production-ready +and scalable claims. + +## Kubernetes-Neutral Baseline + +- Keep domain services stateless between requests. Persist authority in owner + PostgreSQL and durable queues or stores designed for the required semantics. +- Define requests and limits, startup/readiness/liveness probes, graceful shutdown, + termination budgets, PodDisruptionBudget, topology spread, anti-affinity, and + horizontal scaling from measured saturation or backlog signals. +- Apply restricted workload security, non-root execution, read-only filesystems + where possible, explicit service accounts, NetworkPolicy, controlled egress, + signed immutable images, SBOM/provenance, and admission policy. +- Separate migrations from runtime rollout. Use expand/contract migrations, + bounded locks, compatibility windows, and tested rollback or forward-fix paths. +- Autoscaling must account for database connections, queue visibility/leases, + partition ordering, idempotency contention, and provider rate limits. + +## Data And Messaging Scale + +- Size PostgreSQL pools across all replicas; inspect plans, indexes, lock duration, + vacuum pressure, transaction age, replication lag, and backup/restore throughput. +- Partition only with a documented key, pruning evidence, migration path, and + tenant-skew analysis. Sharding never weakens owner invariants or auditability. +- Redis is non-authoritative. Namespace keys, define TTL and eviction behavior, + cap retries, monitor memory and queue lag, and design poison-message isolation. +- Outbox/Inbox publishers use leases, bounded batches, retry backoff, dedupe, and + backlog metrics. Backpressure must fail predictably instead of dropping work. + +## AWS Adapter + +- Keep AWS-specific resources in `operations`. Map the neutral baseline to EKS, + managed PostgreSQL, managed Redis, KMS, workload identity, Secrets Manager or + External Secrets, object storage, load balancing, and native audit telemetry. +- Verify multi-AZ behavior, encryption keys, IAM least privilege, private network + paths, egress controls, database failover, backup retention, restore testing, + quota limits, cost alarms, and regional recovery assumptions. + +## Observability And Operations + +Correlate tenant-safe metrics, logs, and traces with request, job, event, +idempotency, causation, and provider references. Alert on SLO burn, error rate, +latency, saturation, connection exhaustion, queue lag, DLQ, Outbox backlog, +projection lag, failed or expired payment processes, reconciliation mismatch, +migration failure, backup failure, and certificate or key expiry. diff --git a/.agents/skills/mavula-cloud-banking/references/composability-no-code.md b/.agents/skills/mavula-cloud-banking/references/composability-no-code.md new file mode 100644 index 0000000..89060f2 --- /dev/null +++ b/.agents/skills/mavula-cloud-banking/references/composability-no-code.md @@ -0,0 +1,46 @@ +# Composability And Governed No-Code + +## Composable Boundaries + +- Expose capabilities through owner-controlled, versioned APIs, commands, events, + and file contracts with explicit schemas, permissions, errors, and idempotency. +- Prefer additive evolution. Breaking changes require a new version, migration + window, consumer inventory, compatibility tests, and deprecation evidence. +- Keep provider, cloud, channel, and legacy specifics behind adapters. Domain + modules depend on stable ports and business contracts, not vendor SDK objects. +- Use correlation and causation across boundaries. Do not leak database models, + internal enums, queue payloads, secrets, or unnecessary PII into public contracts. +- SDKs and Developer Docs are generated or verified against owner contracts and + provenance locks. Documentation cannot redefine owner behavior. + +## Configuration Lifecycle + +1. Create a tenant-scoped draft from an approved schema. +2. Validate syntax, types, references, permissions, limits, effective dates, + compatibility, and deterministic evaluation before persistence. +3. Preview or dry-run with synthetic or authorized data and no external side effects. +4. Require maker-checker for configurations that can change financial behavior, + access, settlement routing, compliance output, or external communication. +5. Publish an immutable version with actor, reason, correlation, content digest, + approval, and effective window. +6. Activate atomically. Running operations retain the exact version they used. +7. Roll back by activating a compatible prior version; never rewrite history. + +## Runtime Safety + +- Prohibit `eval`, `new Function`, arbitrary scripts, unrestricted templates, + dynamic SQL, filesystem/network access, and reflection-based class loading. +- Use an allowlisted expression or decision runtime with typed inputs/outputs, + deterministic time and rounding, complexity limits, execution timeout, memory + budget, recursion limit, and side-effect isolation. +- Validate money precision, currency, timezone, calendars, rate boundaries, + fee ordering, rule conflicts, workflow cycles, and unreachable states. +- Enforce authorization and tenant context during authoring, approval, activation, + execution, export, and rollback. A UI is not a security boundary. + +## No-Code Meaning + +For MAVULA, no-code means governed declarative products, rules, schemas, and +workflows. It does not mean arbitrary user code, direct database editing, or +unreviewed production activation. A future visual builder must emit the same +versioned contracts and pass the same validation and approval pipeline. diff --git a/.agents/skills/mavula-cloud-banking/references/engineering-data.md b/.agents/skills/mavula-cloud-banking/references/engineering-data.md new file mode 100644 index 0000000..6dfc885 --- /dev/null +++ b/.agents/skills/mavula-cloud-banking/references/engineering-data.md @@ -0,0 +1,57 @@ +# Engineering And Data Standards + +## TypeScript And Node.js + +- Preserve strict public types, validated DTOs, async error propagation, bounded + concurrency, graceful shutdown, ESM/CJS compatibility, and workspace build order. +- NestJS scopes and guards must not leak request identity. Prisma client/schema/ + migration changes move together. BullMQ handlers are idempotent and lease-aware. + +## Go + +- Propagate `context.Context`, cancellation, deadlines, trace context, and tenant + authority through every boundary. Bound goroutines and channels; prove race safety. +- Wrap errors without losing classification. Use explicit interfaces, SQL + transactions, pool limits, deterministic clocks, and table-driven tests. + +## Java + +- Define Spring transaction boundaries, isolation, propagation, exception mapping, + bean scope, thread safety, pool limits, timeouts, and serialization compatibility. +- Avoid hidden lazy-loading, broad transactions, reflection-based configuration, + and retries around non-idempotent effects. Use contract and concurrency tests. + +## COBOL + +- Treat copybooks and fixed-width layouts as versioned wire contracts. Verify + encoding, exact offsets, `PIC` precision/scale, signs, packed decimal, overflow, + rounding, header/detail/trailer totals, checksums, and golden files. +- Batch execution is restartable from durable checkpoints. Duplicate input, + partial output, invalid trailer, and resume never duplicate financial effects. + +## Python + +- Require typed boundaries, reproducible packaging, parameterized SQL, deterministic + timezone and decimal handling, explicit resource cleanup, isolated tests, and + bounded memory for reporting or batch workloads. + +## PostgreSQL + +- Apply tenant RLS inside the same transaction and pooled connection as every + protected query. Runtime roles do not bypass RLS or own schemas. +- Keep financial effects, durable idempotency receipt, audit, and Outbox atomic. + Use constraints as invariants, indexes from measured queries, and online-safe + migrations with lock analysis, compatibility order, and recovery procedure. +- Test concurrent duplicate requests, deadlocks, serialization conflicts, pooled + connection reuse, cross-tenant access, rollback, and migration from real baselines. + +## Redis And Queues + +- Redis never becomes financial or identity truth. Namespace by environment and + tenant where applicable; define TTL, eviction, memory, and failover behavior. +- Jobs carry minimal authorized context, stable idempotency identity, correlation, + bounded retry/backoff, lease timeout, DLQ reason, replay policy, and metrics. + +Go and Java standards are ready for RFC-0003 modules but do not assign ownership +or justify migration by language preference alone. Choose language from workload, +correctness, interoperability, operational maturity, and team support evidence. diff --git a/.agents/skills/mavula-cloud-banking/references/module-ownership.md b/.agents/skills/mavula-cloud-banking/references/module-ownership.md new file mode 100644 index 0000000..07c856c --- /dev/null +++ b/.agents/skills/mavula-cloud-banking/references/module-ownership.md @@ -0,0 +1,29 @@ +# Module Ownership + +## Authority + +| Boundary | Owns | Must not own | +| --- | --- | --- | +| `finance-platform` | Cross-module contracts, policy, submodule revisions, master guardian | Runtime financial or identity state | +| `identity-access` | Institutions, branches, operators, credentials, sessions, memberships, roles, OAuth/OIDC policy | Ledger accounts, payment state, job state | +| `ledger-core` | Tenant financial binding, products, accounts, journals, lending, financial workflows, audit, projections | Credentials, provider settlement state, queue truth | +| `settlements` | Payment processes, provider callbacks, dedupe, reconciliation, settlement Outbox | Journal posting, lending balances, operator identity | +| `workbench` | Jobs, schedules, attempts, leases, DLQ, publisher execution, platform status | Financial aggregates, provider state, identity policy | +| `legacy-connectors` | Copybooks, fixed-width layouts, batch receipts, artifacts, deterministic translation | Direct Identity or Ledger stores, financial mutation | +| `operations` | Kubernetes, AWS adapters, secrets wiring, migrations, backups, monitoring | Business aggregates or domain policy | +| `developer-docs` | Approved public contracts, integration guides, examples, provenance locks | Owner contract invention or private endpoint publication | + +## Integration Rules + +- Integrate through an owner API, authorized command, active event, or approved + versioned file contract. Never share or write another owner's tables. +- Commands carry authenticated tenant, actor, correlation, and idempotency + context. Events carry immutable facts and jobs carry operational work. +- Events use at-least-once delivery. Consumers dedupe persistently before effects. +- Read projections are rebuildable and eventually consistent. They are forbidden + for posting, balance mutation, lending approval, allocation, or settlement + decisions requiring current owner state. +- Cross-owner changes require producer and consumer compatibility tests, + explicit migration order, observability, and a rollback or forward-fix path. +- Ownership changes require an ADR, data migration, compatibility window, and + accountable approval. A folder move does not transfer authority. diff --git a/.agents/skills/mavula-cloud-banking/references/security-regulation.md b/.agents/skills/mavula-cloud-banking/references/security-regulation.md new file mode 100644 index 0000000..e871706 --- /dev/null +++ b/.agents/skills/mavula-cloud-banking/references/security-regulation.md @@ -0,0 +1,53 @@ +# Security And Regulation + +## Mandatory Platform Controls + +- Default deny at API, service, database, queue, and operator boundaries. +- Verify subject and workload identity for each protected resource; network + location is not authority. +- Derive tenant and permissions from signed identity context and enforce RLS in + the same transaction and connection as protected PostgreSQL access. +- Separate runtime, migration, backup, and operator credentials. Runtime roles do + not own schemas, bypass RLS, or receive migration secrets. +- Use least privilege, short-lived credentials, managed key rotation, TLS, secure + webhook verification, dependency provenance, and minimal CI permissions. +- Classify data, minimize event payloads, encrypt sensitive data, define retention + and deletion, and prevent secrets or PII from logs, metrics, traces, examples, + review comments, and `.env` tracking. +- Record append-only audit evidence for authentication, authorization, approvals, + configuration, posting, adjustment, settlement, export, replay, and failure. + +## Financial API Baseline + +- Treat OpenID FAPI 2.0 Security Profile and its attacker model as the target + baseline for high-value external APIs. Assess PAR, PKCE, sender-constrained + tokens, client authentication, redirect handling, key lifecycle, and resource + server behavior before claiming alignment. +- Use NIST SP 800-207 for zero-trust decisions and NIST SP 800-218 for secure + development and supply-chain evidence. +- Apply PCI DSS v4.0.1 only when cardholder data, sensitive authentication data, + or systems affecting that environment are in scope. First minimize and document scope. +- Use ISO 20022 for applicable external financial messaging profiles. It does not + replace MAVULA owner APIs, internal event envelopes, or local regulatory contracts. +- Use CNCF cloud-native security guidance for workload identity, orchestration, + image, runtime, storage, policy, and observability controls. + +## Mozambique Baseline + +- Use the primary Banco de Mocambique and statutory sources already approved in + RFC-0002 for AML/CFT/CPF, transaction records, retention, institutional conduct, + licensing, and credit-registry requirements. +- AML alerts, decisions, beneficial-owner data, suspicious-operation details, and + authority references are restricted need-to-know data, not free-form metadata. +- Preserve applicable records and investigations according to approved policy; + technical defaults never override a legal hold or authority instruction. +- Every legal interpretation, retention exception, reporting obligation, or + compliance claim requires current source verification and accountable legal or + compliance approval. Engineering evidence is not a legal opinion. + +## Required Threat Cases + +Test issuer/audience confusion, stolen or replayed tokens, privilege escalation, +cross-tenant access, self-approval, webhook replay, idempotency fingerprint +conflict, injection, SSRF, unsafe deserialization, secret leakage, dependency +compromise, queue poisoning, unauthorized replay, audit tampering, and backup exposure. diff --git a/.agents/skills/mavula-review/SKILL.md b/.agents/skills/mavula-review/SKILL.md index b50f692..c1d5d6c 100644 --- a/.agents/skills/mavula-review/SKILL.md +++ b/.agents/skills/mavula-review/SKILL.md @@ -1,24 +1,37 @@ --- name: mavula-review -description: Review MAVULA changes across finance-platform, ledger-core, workbench, settlements, operations, and related repositories. Use for pull request reviews, pre-merge checks, security review, database review, migration review, and implementation review in TypeScript, Go, Java, COBOL, Python, PostgreSQL, Redis, Kubernetes, and CI/CD. +description: Review MAVULA changes across finance-platform, identity-access, ledger-core, workbench, settlements, operations, legacy-connectors, and developer-docs. Use for pull request, security, database, migration, contract, cloud-native, scalability, no-code configuration, and production-readiness reviews in TypeScript, Go, Java, COBOL, Python, PostgreSQL, Redis, Kubernetes, AWS, and CI/CD. --- # MAVULA Review -Use this skill to review MAVULA changes with a finance-grade standard: correctness, security, data safety, maintainability, and module ownership come first. +Use this skill to review MAVULA changes against the platform's financial, +security, regulatory, and operational invariants. Findings require a concrete +failure mode and evidence; labels such as secure, compliant, cloud-native, or +scalable are never accepted without proof. + +## Required Context + +Read the relevant references from the companion skill before reviewing: + +- Ownership or cross-module changes: `../mavula-cloud-banking/references/module-ownership.md`. +- User or operator workflows: `../mavula-cloud-banking/references/banking-operations.md`. +- Authentication, authorization, data, AML, or regulatory changes: `../mavula-cloud-banking/references/security-regulation.md`. +- Deployments, queues, performance, availability, or production claims: `../mavula-cloud-banking/references/cloud-native-scale.md`. +- Product, rule, schema, workflow, or no-code changes: `../mavula-cloud-banking/references/composability-no-code.md`. +- Language, PostgreSQL, Redis, or batch changes: `../mavula-cloud-banking/references/engineering-data.md`. ## Review Workflow 1. Read the diff, surrounding code, tests, contracts, migrations, and CI configuration touched by the change. -2. Identify the runtime boundary: application code, ledger/domain logic, settlement process, infrastructure, database, security, or tooling. -3. Check whether the change preserves module ownership: - - `finance-platform` coordinates repository policy, submodules, contracts, and master guardian checks. - - `ledger-core` owns ledger and financial invariants. - - `workbench` owns orchestration and operator runtime. - - `settlements` owns payment and settlement process state. - - `operations` owns deployment, secrets wiring, monitoring, and infrastructure. -4. Verify tests and commands appropriate to the change. Prefer local scripts already defined in `package.json`, module guardians, targeted tests, and CI-equivalent checks. -5. Report only actionable findings. Avoid praise, broad summaries, speculative rewrites, or style-only comments unless they block maintainability or policy. +2. Trace the complete operation from authenticated entry to owner transaction, + emitted contract, asynchronous processing, audit evidence, and recovery path. +3. Verify ownership, tenant isolation, financial invariants, compatibility, + failure handling, operability, capacity evidence, and regulatory applicability. +4. Confirm the smallest test set that proves the behavior, then run broader + guardian and CI-equivalent checks when the change crosses boundaries. +5. Report actionable findings only. Do not replace evidence with architecture + preference, praise, phase narration, or speculative rewrites. ## Findings @@ -36,9 +49,9 @@ Verification: command or test that should cover it. Severity guide: -- `P0`: exploitable security issue, data loss, financial invariant break, or production-wide outage. -- `P1`: build/CI break, incorrect money movement, tenant isolation failure, migration failure, or deploy blocker. -- `P2`: realistic runtime bug, retry/idempotency issue, race, performance risk, observability gap, or missing required test. +- `P0`: exploitable security issue, cross-tenant disclosure, data loss, financial invariant break, or production-wide outage. +- `P1`: build/CI break, incorrect money movement, authorization bypass, unsafe migration, broken recovery, or production deploy blocker. +- `P2`: realistic runtime bug, replay/idempotency issue, race, capacity risk, observability gap, contract drift, or missing required test. - `P3`: maintainability issue with concrete future cost. If there are no findings, say so directly and list residual risk or unrun validation. @@ -49,44 +62,26 @@ Use the language already used in the pull request, issue, or discussion. If the Avoid marketing claims, decorative status symbols, phase narration, and long background explanations in review comments. -## Technical Focus - -For TypeScript and Node.js: - -- Check async error paths, unhandled promises, transaction boundaries, Prisma schema/client drift, ESM/CJS boundaries, workspace package builds, and typed public exports. -- Validate idempotency keys, webhook dedupe, outbox/inbox behavior, retries, and DLQ paths. - -For Go: - -- Check `context.Context` propagation, cancellation, goroutine lifetime, race risks, error wrapping, interface boundaries, SQL transaction handling, and deterministic tests. - -For Java: - -- Check transaction annotations and boundaries, exception mapping, thread safety, serialization compatibility, dependency injection scope, and database connection handling. - -For COBOL: - -- Check `PIC` precision and scale, signed and packed decimal fields, copybook compatibility, file layouts, batch restartability, commit points, and reconciliation totals. - -For Python: - -- Check type coverage, packaging metadata, resource cleanup, deterministic migrations, SQL parameterization, timezone handling, and test isolation. - -## Data And Security - -Treat database and security review as required for finance changes. - -Check database changes for: - -- Backward-compatible migrations, rollback path, generated clients, indexes, constraints, foreign keys, locks, long-running statements, and online deploy safety. -- Tenant isolation, row-level policy assumptions, idempotency, replay safety, outbox/inbox atomicity, webhook dedupe, and reconciliation paths. -- PostgreSQL query plans where cardinality, indexes, or locks can affect production behavior. -- Redis key namespacing, TTLs, retry counters, queue semantics, and poison-message behavior. - -Check security changes for: - -- Authentication, authorization, tenant boundaries, least privilege, secret handling, audit logs, dependency and supply-chain risk, CI token permissions, webhook signatures, injection, SSRF, crypto/TLS use, and PII leakage. -- No credentials, tokens, private keys, customer data, or `.env` files may be committed or shown in review output. +## Non-Negotiable Review Gates + +- Reject tenant, institution, branch, role, or permission authority derived from + request payloads instead of verified identity context. +- Reject floating-point money, unbalanced journals, mutable posted entries, + self-approval, non-atomic audit, or duplicate effects for one idempotency key. +- Reject direct writes to another module's store and jobs or projections used as + the source of truth for command-side financial decisions. +- Reject webhook effects without signature validation, replay protection, + durable dedupe, explicit transitions, and reconciliation. +- Reject `eval`, `new Function`, mutable published configuration, or unbounded + expressions in configurable and no-code runtimes. +- Reject exactly-once assumptions. Require bounded retry, idempotent handlers, + poison-message handling, DLQ policy, authorized replay, and correlation. +- Reject production-ready or scalable claims without quantified service SLO, + workload, data growth, RTO/RPO, failure-domain, and capacity-test evidence. +- Reject compliance or certification claims without scope, control evidence, + responsible approval, and legal validation when required. +- Never expose credentials, tokens, private keys, customer data, or untracked + `.env` content in code or review output. ## Validation Expectations @@ -99,10 +94,14 @@ pnpm -r build git diff --check ``` -For module changes, run the module-specific guardian and targeted tests before broader builds when possible. +For module changes, run the module guardian and targeted tests first. For +cross-repository changes, validate owner contracts, consumer compatibility, +master guardian, and initialized submodule parity. ## Agent Behavior -Do not approve your own change. Do not bypass branch protection unless explicitly instructed by a repository owner for a concrete blocked merge. +Do not approve your own change. Do not bypass branch protection unless an +authorized repository owner explicitly requests a narrowly scoped exception for +a concrete blocked merge. Checks and conversation resolution remain mandatory. When reviewing a pull request, keep the output review-shaped: findings first, then open questions, then validation notes. When implementing a fix, keep edits scoped to the issue, preserve unrelated local work, and update tests or guardian rules when the risk justifies it. diff --git a/.agents/skills/mavula-review/agents/openai.yaml b/.agents/skills/mavula-review/agents/openai.yaml index 6139e49..46a0f43 100644 --- a/.agents/skills/mavula-review/agents/openai.yaml +++ b/.agents/skills/mavula-review/agents/openai.yaml @@ -1,4 +1,4 @@ interface: display_name: "MAVULA Review" - short_description: "Review MAVULA changes across code, data, and security" - default_prompt: "Use $mavula-review to review this MAVULA change for correctness, data safety, security, and maintainability." + short_description: "Review regulated banking changes against strict evidence gates" + default_prompt: "Use $mavula-review to review this MAVULA change for financial correctness, module ownership, tenant and data safety, security, contracts, operability, and production evidence." diff --git a/.cursor/rules/mavula-engineering.mdc b/.cursor/rules/mavula-engineering.mdc new file mode 100644 index 0000000..8063d6b --- /dev/null +++ b/.cursor/rules/mavula-engineering.mdc @@ -0,0 +1,12 @@ +--- +description: MAVULA regulated cloud-banking engineering and review guardrails +alwaysApply: true +--- + +Use `.agents/skills/mavula-cloud-banking/SKILL.md` for MAVULA architecture, +implementation, operations, security, scalability, composability, and no-code +configuration work. + +Use `.agents/skills/mavula-review/SKILL.md` for reviews. Production-impact +reviews must apply both skills. Lead with actionable findings ordered by +severity and use the language of the pull request, issue, or discussion. diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..c5857f8 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,9 @@ +# MAVULA Agent Instructions + +For architecture, implementation, operations, security, scalability, +composability, and no-code configuration, follow +`.agents/skills/mavula-cloud-banking/SKILL.md`. + +For reviews, follow `.agents/skills/mavula-review/SKILL.md` and load the relevant +cloud-banking references. Prioritize financial invariants, ownership, tenant and +data safety, security, recovery, capacity evidence, and CI reproducibility. diff --git a/.gitignore b/.gitignore index 0168d4d..bd27d84 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,17 @@ test-results/ !.agents/skills/mavula-review/agents/ .agents/skills/mavula-review/agents/* !.agents/skills/mavula-review/agents/openai.yaml +!.agents/skills/mavula-cloud-banking/ +.agents/skills/mavula-cloud-banking/* +!.agents/skills/mavula-cloud-banking/SKILL.md +!.agents/skills/mavula-cloud-banking/agents/ +.agents/skills/mavula-cloud-banking/agents/* +!.agents/skills/mavula-cloud-banking/agents/openai.yaml +!.agents/skills/mavula-cloud-banking/references/ +.agents/skills/mavula-cloud-banking/references/* +!.agents/skills/mavula-cloud-banking/references/module-ownership.md +!.agents/skills/mavula-cloud-banking/references/banking-operations.md +!.agents/skills/mavula-cloud-banking/references/security-regulation.md +!.agents/skills/mavula-cloud-banking/references/cloud-native-scale.md +!.agents/skills/mavula-cloud-banking/references/composability-no-code.md +!.agents/skills/mavula-cloud-banking/references/engineering-data.md diff --git a/scripts/check-agent-policy.mjs b/scripts/check-agent-policy.mjs new file mode 100644 index 0000000..e3ddd3e --- /dev/null +++ b/scripts/check-agent-policy.mjs @@ -0,0 +1,61 @@ +import { existsSync, readFileSync } from "node:fs"; +import { spawnSync } from "node:child_process"; + +export const canonicalAgentFiles = Object.freeze([ + ".agents/AGENTS.md", + ".agents/skills/mavula-review/SKILL.md", + ".agents/skills/mavula-review/agents/openai.yaml", + ".agents/skills/mavula-cloud-banking/SKILL.md", + ".agents/skills/mavula-cloud-banking/agents/openai.yaml", + ".agents/skills/mavula-cloud-banking/references/module-ownership.md", + ".agents/skills/mavula-cloud-banking/references/banking-operations.md", + ".agents/skills/mavula-cloud-banking/references/security-regulation.md", + ".agents/skills/mavula-cloud-banking/references/cloud-native-scale.md", + ".agents/skills/mavula-cloud-banking/references/composability-no-code.md", + ".agents/skills/mavula-cloud-banking/references/engineering-data.md", +]); + +export const canonicalAgentAdapters = Object.freeze([ + ".cursor/rules/mavula-engineering.mdc", + ".github/copilot-instructions.md", +]); + +export function enforceLocalAgentPolicy() { + const failures = []; + const required = [...canonicalAgentFiles, ...canonicalAgentAdapters]; + for (const file of required) { + if (!existsSync(file)) failures.push(`${file} is required`); + } + + const tracked = spawnSync("git", ["ls-files"], { encoding: "utf8" }); + if (tracked.status !== 0) return ["git ls-files failed for agent policy"]; + const trackedFiles = new Set(tracked.stdout.split("\n").filter(Boolean)); + + for (const file of required) { + if (!trackedFiles.has(file)) failures.push(`${file} must be tracked`); + } + + const allowedAgents = new Set(canonicalAgentFiles); + for (const file of trackedFiles) { + if (file.startsWith(".agents/") && !allowedAgents.has(file)) { + failures.push(`${file} is not part of the canonical agent policy`); + } + if (file.startsWith(".cursor/rules/") && file !== canonicalAgentAdapters[0]) { + failures.push(`${file} is not part of the canonical Cursor policy`); + } + } + + for (const file of [ + ".agents/AGENTS.md", + ".cursor/rules/mavula-engineering.mdc", + ".github/copilot-instructions.md", + ]) { + if (!existsSync(file)) continue; + const content = readFileSync(file, "utf8"); + for (const skill of ["mavula-review", "mavula-cloud-banking"]) { + if (!content.includes(skill)) failures.push(`${file} must route to ${skill}`); + } + } + + return failures; +} diff --git a/scripts/guardian.mjs b/scripts/guardian.mjs index 17ee9be..fe59edb 100644 --- a/scripts/guardian.mjs +++ b/scripts/guardian.mjs @@ -1,8 +1,10 @@ #!/usr/bin/env node import { existsSync, readFileSync } from 'node:fs'; import { spawnSync } from 'node:child_process'; +import { enforceLocalAgentPolicy } from './check-agent-policy.mjs'; const failures = []; +failures.push(...enforceLocalAgentPolicy()); const required = [ '.agents/AGENTS.md', '.agents/skills/mavula-review/SKILL.md', '.agents/skills/mavula-review/agents/openai.yaml', '.github/CODEOWNERS', From 5b846df8038e39af1a79096929fa49b30effc4e3 Mon Sep 17 00:00:00 2001 From: Estandar <143508382+EstandarMustaq@users.noreply.github.com> Date: Sat, 18 Jul 2026 11:57:34 +0200 Subject: [PATCH 2/3] fix: enforce root-scoped agent policy --- .gitignore | 1 - .agents/AGENTS.md => AGENTS.md | 0 scripts/check-agent-policy.mjs | 52 +++++++++++++++++++++++++++++----- scripts/guardian.mjs | 3 +- 4 files changed, 46 insertions(+), 10 deletions(-) rename .agents/AGENTS.md => AGENTS.md (100%) diff --git a/.gitignore b/.gitignore index bd27d84..592adb4 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,6 @@ test-results/ .env.* !.env.example .agents/* -!.agents/AGENTS.md !.agents/skills/ .agents/skills/* !.agents/skills/mavula-review/ diff --git a/.agents/AGENTS.md b/AGENTS.md similarity index 100% rename from .agents/AGENTS.md rename to AGENTS.md diff --git a/scripts/check-agent-policy.mjs b/scripts/check-agent-policy.mjs index e3ddd3e..fe64333 100644 --- a/scripts/check-agent-policy.mjs +++ b/scripts/check-agent-policy.mjs @@ -1,8 +1,10 @@ import { existsSync, readFileSync } from "node:fs"; import { spawnSync } from "node:child_process"; +import { createHash } from "node:crypto"; +import { join } from "node:path"; export const canonicalAgentFiles = Object.freeze([ - ".agents/AGENTS.md", + "AGENTS.md", ".agents/skills/mavula-review/SKILL.md", ".agents/skills/mavula-review/agents/openai.yaml", ".agents/skills/mavula-cloud-banking/SKILL.md", @@ -20,19 +22,47 @@ export const canonicalAgentAdapters = Object.freeze([ ".github/copilot-instructions.md", ]); -export function enforceLocalAgentPolicy() { +export const canonicalAgentDigests = Object.freeze({ + "AGENTS.md": "8ea1de953a05a947f0cbe5a05ea0c5d4d98ef5fa5121611cc319cad5fcec2a86", + ".agents/skills/mavula-review/SKILL.md": "225491092735e63ae996ee56ea550ca8a3385688b577a34c1ca3346ae32fd8f1", + ".agents/skills/mavula-review/agents/openai.yaml": "8d9e7ded6558f9c57032d4654385fcf889911dbec3108c736d377ff31b536b1a", + ".agents/skills/mavula-cloud-banking/SKILL.md": "610a2f2154839389aa67c550c17cde9f72296b012f2e89006937bf64c41ef355", + ".agents/skills/mavula-cloud-banking/agents/openai.yaml": "0eceb6e2d4ce0ee09754bea0cb3ce71a96c22a2f1c3a62202c1561be0f1bcefd", + ".agents/skills/mavula-cloud-banking/references/banking-operations.md": "000f033ca5810368513c0e53812ee9ee489565cb7b6c6589cce6ddb21748e39f", + ".agents/skills/mavula-cloud-banking/references/cloud-native-scale.md": "54ff285d608844090883d2535988980fe84f398a6654c1e5ba1cfa52bccecb1a", + ".agents/skills/mavula-cloud-banking/references/composability-no-code.md": "2a05a498b1145c13dd77f83746fab906fae7dacaa8f177e30a8b537e805bff6c", + ".agents/skills/mavula-cloud-banking/references/engineering-data.md": "1f8ead9ddd44a67e4c5db465ad1da4f15495dc40d8c079ca88737683fe372534", + ".agents/skills/mavula-cloud-banking/references/module-ownership.md": "b8f1f85b54e1b0878912e76d377c9b1484c7a685ee6299344429e4b7d8c67eb9", + ".agents/skills/mavula-cloud-banking/references/security-regulation.md": "7f6ed9f897b1b6acf98e29687f4744da8fbdf21edfef8f75f217f938edb7bb20", + ".cursor/rules/mavula-engineering.mdc": "5808eae2c61ed71c9986f238a8df46a2dd5d47fdb144dfdb5d1ab59e8845a736", + ".github/copilot-instructions.md": "e559442e010dd5baf747999f51e828bf9224c4a990cb83c0322f99986cbe87e1", +}); + +function sha256(file) { + return createHash("sha256").update(readFileSync(file)).digest("hex"); +} + +export function enforceLocalAgentPolicy({ root = "." } = {}) { const failures = []; const required = [...canonicalAgentFiles, ...canonicalAgentAdapters]; for (const file of required) { - if (!existsSync(file)) failures.push(`${file} is required`); + if (!existsSync(join(root, file))) failures.push(`${file} is required`); } - const tracked = spawnSync("git", ["ls-files"], { encoding: "utf8" }); + const tracked = spawnSync("git", ["ls-files"], { cwd: root, encoding: "utf8" }); if (tracked.status !== 0) return ["git ls-files failed for agent policy"]; const trackedFiles = new Set(tracked.stdout.split("\n").filter(Boolean)); for (const file of required) { if (!trackedFiles.has(file)) failures.push(`${file} must be tracked`); + const absolutePath = join(root, file); + if (!existsSync(absolutePath)) continue; + const expectedDigest = canonicalAgentDigests[file]; + if (!expectedDigest) { + failures.push(`${file} has no approved canonical digest`); + } else if (sha256(absolutePath) !== expectedDigest) { + failures.push(`${file} differs from the approved canonical content`); + } } const allowedAgents = new Set(canonicalAgentFiles); @@ -43,15 +73,23 @@ export function enforceLocalAgentPolicy() { if (file.startsWith(".cursor/rules/") && file !== canonicalAgentAdapters[0]) { failures.push(`${file} is not part of the canonical Cursor policy`); } + if (file.startsWith(".github/instructions/")) { + failures.push(`${file} is not part of the canonical Copilot policy`); + } + const basename = file.split("/").at(-1); + if (["AGENTS.md", "AGENTS.override.md"].includes(basename) && file !== "AGENTS.md") { + failures.push(`${file} conflicts with the root agent entry point`); + } } for (const file of [ - ".agents/AGENTS.md", + "AGENTS.md", ".cursor/rules/mavula-engineering.mdc", ".github/copilot-instructions.md", ]) { - if (!existsSync(file)) continue; - const content = readFileSync(file, "utf8"); + const absolutePath = join(root, file); + if (!existsSync(absolutePath)) continue; + const content = readFileSync(absolutePath, "utf8"); for (const skill of ["mavula-review", "mavula-cloud-banking"]) { if (!content.includes(skill)) failures.push(`${file} must route to ${skill}`); } diff --git a/scripts/guardian.mjs b/scripts/guardian.mjs index fe59edb..eedb769 100644 --- a/scripts/guardian.mjs +++ b/scripts/guardian.mjs @@ -6,8 +6,7 @@ import { enforceLocalAgentPolicy } from './check-agent-policy.mjs'; const failures = []; failures.push(...enforceLocalAgentPolicy()); const required = [ - '.agents/AGENTS.md', '.agents/skills/mavula-review/SKILL.md', - '.agents/skills/mavula-review/agents/openai.yaml', '.github/CODEOWNERS', + '.github/CODEOWNERS', '.github/PULL_REQUEST_TEMPLATE.md', '.github/workflows/guardian.yml', '.github/workflows/required-ci.yml', '.github/workflows/pages.yml', 'LICENSE', 'README.md', 'package.json', 'sources.lock.json', 'redocly.yaml', From 7b47696fefe7ef967a26d9759fe48670fece1884 Mon Sep 17 00:00:00 2001 From: Estandar <143508382+EstandarMustaq@users.noreply.github.com> Date: Sat, 18 Jul 2026 13:59:46 +0200 Subject: [PATCH 3/3] fix: block alternate agent instruction sources --- CLAUDE.md | 10 ++++ package.json | 2 +- scripts/check-agent-policy.mjs | 20 ++++++- scripts/check-agent-policy.test.mjs | 92 +++++++++++++++++++++++++++++ 4 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 CLAUDE.md create mode 100644 scripts/check-agent-policy.test.mjs diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..d272f8b --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,10 @@ +# MAVULA Agent Instructions + +Follow `AGENTS.md` as the repository-wide entry point. + +Use `.agents/skills/mavula-cloud-banking/SKILL.md` for architecture, +implementation, operations, security, scalability, composability, and no-code +configuration. Use `.agents/skills/mavula-review/SKILL.md` for reviews and load +the relevant cloud-banking references for production-impact changes. + +Do not create additional Claude instruction files or rules in this repository. diff --git a/package.json b/package.json index 2907cfa..ddf5086 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "scripts": { "dev": "astro dev", "preview": "astro preview", - "guardian:check": "node scripts/guardian.mjs", + "guardian:check": "node scripts/guardian.mjs && node --test scripts/check-agent-policy.test.mjs", "contracts:check": "node scripts/check-contracts.mjs", "build": "node scripts/build-docs.mjs", "test": "node --test test/*.test.mjs", diff --git a/scripts/check-agent-policy.mjs b/scripts/check-agent-policy.mjs index fe64333..13245a5 100644 --- a/scripts/check-agent-policy.mjs +++ b/scripts/check-agent-policy.mjs @@ -20,6 +20,7 @@ export const canonicalAgentFiles = Object.freeze([ export const canonicalAgentAdapters = Object.freeze([ ".cursor/rules/mavula-engineering.mdc", ".github/copilot-instructions.md", + "CLAUDE.md", ]); export const canonicalAgentDigests = Object.freeze({ @@ -36,6 +37,7 @@ export const canonicalAgentDigests = Object.freeze({ ".agents/skills/mavula-cloud-banking/references/security-regulation.md": "7f6ed9f897b1b6acf98e29687f4744da8fbdf21edfef8f75f217f938edb7bb20", ".cursor/rules/mavula-engineering.mdc": "5808eae2c61ed71c9986f238a8df46a2dd5d47fdb144dfdb5d1ab59e8845a736", ".github/copilot-instructions.md": "e559442e010dd5baf747999f51e828bf9224c4a990cb83c0322f99986cbe87e1", + "CLAUDE.md": "b450943d31e250744342e8162d2418883ce8f3792a863e5d3353ef94f7c2fd13", }); function sha256(file) { @@ -67,6 +69,9 @@ export function enforceLocalAgentPolicy({ root = "." } = {}) { const allowedAgents = new Set(canonicalAgentFiles); for (const file of trackedFiles) { + if (file === ".cursorrules") { + failures.push(`${file} conflicts with the canonical Cursor policy`); + } if (file.startsWith(".agents/") && !allowedAgents.has(file)) { failures.push(`${file} is not part of the canonical agent policy`); } @@ -76,16 +81,29 @@ export function enforceLocalAgentPolicy({ root = "." } = {}) { if (file.startsWith(".github/instructions/")) { failures.push(`${file} is not part of the canonical Copilot policy`); } + if (file.startsWith(".claude/")) { + failures.push(`${file} conflicts with the canonical Claude policy`); + } const basename = file.split("/").at(-1); - if (["AGENTS.md", "AGENTS.override.md"].includes(basename) && file !== "AGENTS.md") { + if ( + (["AGENTS.md", "AGENTS.override.md"].includes(basename) && file !== "AGENTS.md") + || (basename === "CLAUDE.md" && file !== "CLAUDE.md") + ) { failures.push(`${file} conflicts with the root agent entry point`); } + if (file === ".vscode/settings.json") { + const settings = readFileSync(join(root, file), "utf8"); + if (/"(?:chat\.instructionsFilesLocations|github\.copilot\.chat\.[^"]*\.instructions)"\s*:/.test(settings)) { + failures.push(`${file} contains an alternate VS Code instruction source`); + } + } } for (const file of [ "AGENTS.md", ".cursor/rules/mavula-engineering.mdc", ".github/copilot-instructions.md", + "CLAUDE.md", ]) { const absolutePath = join(root, file); if (!existsSync(absolutePath)) continue; diff --git a/scripts/check-agent-policy.test.mjs b/scripts/check-agent-policy.test.mjs new file mode 100644 index 0000000..0b3f1c1 --- /dev/null +++ b/scripts/check-agent-policy.test.mjs @@ -0,0 +1,92 @@ +import assert from "node:assert/strict"; +import { cpSync, mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { dirname, join } from "node:path"; +import { spawnSync } from "node:child_process"; +import test from "node:test"; +import { fileURLToPath } from "node:url"; +import { + canonicalAgentAdapters, + canonicalAgentFiles, + enforceLocalAgentPolicy, +} from "./check-agent-policy.mjs"; + +const sourceRoot = join(dirname(fileURLToPath(import.meta.url)), ".."); + +function withPolicyRepository(mutate, verify) { + const root = mkdtempSync(join(tmpdir(), "mavula-agent-policy-")); + try { + for (const file of [...canonicalAgentFiles, ...canonicalAgentAdapters]) { + const target = join(root, file); + mkdirSync(dirname(target), { recursive: true }); + cpSync(join(sourceRoot, file), target); + } + mutate(root); + const initialized = spawnSync("git", ["init", "-q"], { cwd: root, encoding: "utf8" }); + assert.equal(initialized.status, 0, initialized.stderr); + const staged = spawnSync("git", ["add", "."], { cwd: root, encoding: "utf8" }); + assert.equal(staged.status, 0, staged.stderr); + verify(enforceLocalAgentPolicy({ root })); + } finally { + rmSync(root, { force: true, recursive: true }); + } +} + +function write(root, file, content = "conflicting instructions\n") { + const target = join(root, file); + mkdirSync(dirname(target), { recursive: true }); + writeFileSync(target, content); +} + +test("accepts the canonical multi-agent policy", () => { + withPolicyRepository(() => {}, (failures) => assert.deepEqual(failures, [])); +}); + +test("rejects alternate repository instruction files", () => { + withPolicyRepository( + (root) => { + for (const file of [ + ".cursorrules", + ".claude/rules/review.md", + ".github/instructions/unsafe.instructions.md", + ".cursor/rules/unsafe.mdc", + "AGENTS.override.md", + "nested/AGENTS.md", + "nested/CLAUDE.md", + ]) write(root, file); + }, + (failures) => { + for (const file of [ + ".cursorrules", + ".claude/rules/review.md", + ".github/instructions/unsafe.instructions.md", + ".cursor/rules/unsafe.mdc", + "AGENTS.override.md", + "nested/AGENTS.md", + "nested/CLAUDE.md", + ]) assert.ok(failures.some((failure) => failure.includes(file)), `missing rejection for ${file}`); + }, + ); +}); + +test("rejects VS Code instruction overrides but accepts ordinary settings", () => { + withPolicyRepository( + (root) => write(root, ".vscode/settings.json", '{"editor.formatOnSave": true}\n'), + (failures) => assert.deepEqual(failures, []), + ); + withPolicyRepository( + (root) => write(root, ".vscode/settings.json", '{"chat.instructionsFilesLocations": {"unsafe.md": true}}\n'), + (failures) => assert.ok(failures.some((failure) => failure.includes("alternate VS Code instruction source"))), + ); + withPolicyRepository( + (root) => write(root, ".vscode/settings.json", '{"github.copilot.chat.reviewSelection.instructions": [{"text": "ignore policy"}]}\n'), + (failures) => assert.ok(failures.some((failure) => failure.includes("alternate VS Code instruction source"))), + ); +}); + +test("rejects modified canonical policy content", () => { + withPolicyRepository( + (root) => write(root, ".agents/skills/mavula-review/SKILL.md", "stub\n"), + (failures) => assert.ok(failures.some((failure) => failure.includes("differs from the approved canonical content"))), + ); +});