Lex Origin Readiness

generated — UTC

Lex Origin — Readiness Engine

Automated readiness-check engine for the Lex Origin MVP. Reads the signed contract spec (Annexure A scope + Annexure C acceptance), probes the running platform (file presence, controller decorators, HTTP endpoints, response latency), and emits a client-shareable Markdown report + machine-readable JSON for regression tracking.

The point: you don't re-generate a status report by hand every week. Change the code, run npm run readiness, and the report regenerates with evidence against the signed scope.


Install

cd readiness
npm install

Run

npm run readiness             # full run — scope + acceptance
npm run readiness:scope       # only Annexure A scope
npm run readiness:acceptance  # only Annexure C hard metrics
npm run readiness:verbose     # extra per-check logging

Output:

Environment variables

Var Purpose
LEX_ORIGIN_ROOT Repo root path (default C:/Lex Origin). Override for CI.
AUTH_BEARER Bearer token used by http_* checks that declare bearer_env: AUTH_BEARER. Not set → those checks skip (not fail).

Static-analysis-only runs (no services up, no token) still produce a useful report — anything that depends on a running service shows as skip, scope coverage by file/grep still works.


How a feature is marked

all pass + no manual-pending   →   ✅ Done
any fail OR all skip           →   look at individual checks
some pass + some fail/pending  →   🟡 Partial
all fail (no pass, no pending) →   ⚪ Not started
no effective checks            →   ❓ Unknown

A feature's status is derived strictly from its checks — no judgement calls in the engine. If the engine's calling something "done" when it shouldn't, the fix is to tighten the check, not override.


Primitive check types

file_exists

- type: file_exists
  path: authservice/src/payroll/payroll.controller.ts
  must_exist: true    # default true; set false to assert absence

grep

- type: grep
  path: authservice/src/cases/case/case.controller.ts
  pattern: "@RequirePracticeType"
  must_exist: true    # default true
  min_matches: 1      # how many files must match

Regex is JS flavor. Pattern is matched against each file's whole contents (not line-by-line). Walks directories recursively, skipping node_modules, .next, .git, dist, build, venv, __pycache__, coverage.

http_status

- type: http_status
  url: http://localhost:8004/api/v1/payroll
  expect_status: 403
  method: GET          # default GET
  bearer_env: AUTH_BEARER
  timeout_ms: 5000     # default 5000

http_latency

- type: http_latency
  url: http://localhost:8010/api/v1/case-law/search?q=cheque+bounce
  threshold_ms: 5000   # Annexure C target
  samples: 5           # default 5
  bearer_env: AUTH_BEARER

Reports avg_ms, p95_ms, samples. Marks pass if avg_ms ≤ threshold_ms.

manual

- type: manual
  question: "Does the RAG pipeline cite real sources?"
  review_cadence_days: 30   # default 30

Looks up a verdict in manual-verdicts.yaml keyed by feature-id + question hash. If missing or older than the cadence, status is manual_pending.


Adding a verdict for a manual check

Edit manual-verdicts.yaml:

verdicts:
  - feature_id: rag-pipelines
    question: "Does the RAG pipeline retrieve and ground answers from the Indian legal corpus? Give a sample query + top-3 citations."
    verdict: pass
    evidence: "See runbook screenshot at docs/evidence/rag-citations-2026-10-05.png — 3 of 3 citations verified against Indian Kanoon."
    reviewed_by: "Arjun"
    reviewed_on: "2026-10-05"

Then re-run npm run readiness. The engine marks it pass until it goes stale (older than review_cadence_days), at which point it re-surfaces as manual_pending.


Adding a new feature or check

  1. Open spec/annexure-a-scope.yaml (or annexure-c-acceptance.yaml).
  2. Append a feature:
    - id: my-new-feature                    # stable kebab-case
      name: "What the client reads"
      annexure: "3.X Section name"
      required_for_mvp: true
      checks:
        - type: file_exists
          path: path/to/file
        - type: manual
          question: "..."
    
  3. Re-run npm run readiness.

Status override (last-resort escape hatch)

For items that genuinely can't be probed by this box — e.g. "VAPT report delivered", "IP assignment signed":

- id: ip-handover
  name: "IPR formally assigned"
  annexure: "3.7 Law Firm Owner — Enterprise"
  checks: []
  status_override:
    status: done
    reason: "Signed IP assignment on file at Company Docs/IP-Assignment-2026-10-05.pdf"
    set_by: "Arjun"
    set_on: "2026-10-05"

Overrides are noisy by design — they show up explicitly in the report ("OVERRIDE: done — reason…") so the Client can tell "I automated this" from "I asserted this".


Regression tracking

Each run writes reports/history/YYYY-MM-DD.json. The next run compares against the most recent prior snapshot and prints regressions (features that got worse) + improvements (features that got better) in the report's "Change since last run" section.

Commit reports/history/*.json to git if you want the client-side delta to live alongside the code history. The latest .md and .json are in .gitignore so they regenerate cleanly.


When this engine is NOT the right answer


File layout

readiness/
├── spec/
│   ├── annexure-a-scope.yaml       # ~55 scope features
│   ├── annexure-c-acceptance.yaml  # 12 hard metrics
│   └── annexure-b-timeline.yaml    # 6 phases (reference only for now)
├── src/
│   ├── types.ts                    # shared TS types
│   ├── primitives.ts               # check implementations
│   ├── runner.ts                   # entry — npm run readiness
│   └── report.ts                   # markdown rendering
├── reports/
│   ├── latest.md                   # regenerated each run
│   ├── latest.json                 # regenerated each run
│   └── history/                    # dated snapshots (keep in git)
├── manual-verdicts.yaml            # the human judgement calls
├── package.json
├── tsconfig.json
└── README.md