Pre-launch. Not deployable to production. This page states what the tests prove and what is missing — nothing else.

How a coding agent finds out what your CRM project actually contains

See what exists

People ask this as: how does an AI agent understand my existing codebase

One command that reads checked-in source and prints a deterministic JSON document describing what an application has composed: packages, the resolved capability graph, records and their revisions, actions, policies, providers. It opens no database, contacts no provider, reads no secret and reports nothing about what is running or deployed. Every one of those blind spots is published in the report itself as a machine-readable limitation code, so an agent can branch on them instead of guessing. In this repository the run takes about 0.27 seconds and writes nothing.

Where this stops

Read this before the rest of the page. Every line below is a thing this does not do.

  • It never opens the configured database. Nothing in the report says which migrations any database has applied, or what its rows contain (DATABASE_NOT_INSPECTED).
  • A providers[] entry means a provider definition was composed in source. It is not evidence of a credential, a network route, an authenticated session or a healthy remote service (PROVIDER_HEALTH_UNKNOWN, SECRETS_NOT_INSPECTED).
  • It is isolation, not a sandbox. Reading a code-first package imports it, so package code runs with the invoking user's full authority over the filesystem, network and credentials of that process (PACKAGE_SOURCE_TRUSTED).
  • It reports no runtime, deployment or authorization state, because none exists to report: there is no authentication, tenancy or RBAC in this framework (PRODUCTION_SPINE_ABSENT, RUNTIME_STATE_UNKNOWN).
  • It does not aggregate evidence. Job status and quality-gate status are prose maintained by people; the report cites their paths and never parses them into claims (EVIDENCE_NOT_AGGREGATED, CI_EVIDENCE_NOT_INFERRED).
  • It plans nothing and changes nothing — no architecture choice, no code written, no package installed, no server started, no action executed.
  • adminExtensions is empty for every project that exists, because the framework has no seam for one. That is a missing capability, not an empty result (ADMIN_EXTENSIONS_UNSUPPORTED).

No authentication, tenancy or RBAC. The server is local-development-only. An actor header is an assertion, not an identity. Do not expose it to a network. Every claim and every limitation is on one page.

What question it answers

"What is actually in this project?" — asked by an agent that has just been pointed at a checkout it has never seen, and that will otherwise assemble the answer from source layout, README prose and a hopeful guess. That reconstruction fails in one direction almost every time: it invents a capability the application does not have, and then builds on it.

The alternative it replaces is five separate surfaces — `crm package inspect` once per package, `/api/schema` from a running server, the checked-in composition file, each package README, and a status document written in prose. `AGENTS.md` rule 13 makes the substitution explicit: to learn what an application has, run the command rather than assembling it from source and prose.

What it does

`npm run crm -- app inspect --json` prints a document stamped `applicationInspectionContract: 1` with a stable top level: `application`, `packages`, `capabilities`, `resources`, `actions`, `policies`, `providers`, `modules`, `adminExtensions`, `problems`, `limitations`, `evidence`. Every list is sorted by identity rather than composition order, and two runs over the same checked-in state produce byte-identical bytes — the suite asserts that rather than promising it.

It keeps five things apart that agents routinely collapse into one word: a package's own release `version`, the `packageContract` seam the kernel enforces, a capability's `version`, a record's `revision` (ADR-019) and a policy or provider's declared-definition `version` (ADR-015). Installing the wrong thing usually starts with treating two of those as the same number.

The capability graph is reported from both ends and — this is the part worth having — including the edges that do not resolve. `status` is `resolved`, `missing` or `provider-mismatch`, decided by the same function `PackageRegistry` throws from at startup, so the inspector and the running application cannot disagree about what a valid composition is.

Reading a code-first package means importing it, so the load runs in a child process that leads its own process group, under a timeout, with the report travelling on file descriptor 3. The child's stdout and stderr are the project's own noise, forwarded to your stderr under a label — which is why a package that logs during import cannot corrupt the document.

Run in this repository on 2026-08-09 it printed `"valid": true`, four core records (approval, company, contact, opportunity), zero packages, zero capabilities, zero actions, zero policies, zero providers, zero problems and eleven limitations. That emptiness is correct and it is the most instructive thing the command does here: the framework repository composes almost nothing, because its composed application exists only as a test helper. A project installed from the starter reports a completely different picture from the same command. An agent that had inferred capability from the source tree would have been more wrong in this repository than in a customer's.

What it refuses to claim

Eleven codes, in `limitations[]`, in the report — not only in the guide. Verbatim from the run: `DATABASE_NOT_INSPECTED` (the configured database is never opened, so what any database has applied or holds is unknown), `PACKAGE_SOURCE_TRUSTED` (package code runs with this process's authority; nothing here is sandboxed), `PROCESS_ISOLATION_BOUNDED` (the timeout stops the load's process group, so a package that deliberately detaches a process outlives the inspection), `EVIDENCE_NOT_AGGREGATED` (jobs and quality-gate status are Markdown maintained by people, referenced by path and never parsed), `CI_EVIDENCE_NOT_INFERRED`, `SECRETS_NOT_INSPECTED`, `PROVIDER_HEALTH_UNKNOWN` (a composed provider is not a reachable one), `PRODUCTION_SPINE_ABSENT` (there is no auth, tenancy or RBAC, so no runtime authorization is reportable), `ADMIN_EXTENSIONS_UNSUPPORTED` (the framework has no seam for package Admin extensions, so that list is empty for every project, not merely this one), `DATA_QUALITY_UNKNOWN` and `RUNTIME_STATE_UNKNOWN`.

Two of those are worth reading twice. `ADMIN_EXTENSIONS_UNSUPPORTED` is a limitation of the framework being reported by the inspector rather than a fact about your project — an empty list that means "impossible" is otherwise indistinguishable from one that means "none yet". And `EVIDENCE_NOT_AGGREGATED` is why `evidence` carries paths and a `status` of `not_aggregated` instead of a verdict: the report points at `docs/QUALITY_GATES.md`, the jobs matrix and the status file, and refuses to summarise them.

`fromStates: null` on an action means the action declares no state-restriction metadata. It does not mean the action is valid in every runtime state; the server may still refuse it from its own rules. The human view spells that out in words for the same reason.

It also does not choose an architecture, produce a plan, modify code, install a package, start a server, deploy, read a secret or execute a business action.

What it costs to run

Measured here on 2026-08-09: about 0.27 s wall clock for `--json`, exit code 0. It writes no file, creates no database and leaves the working tree untouched — `git status` before and after is identical.

Exit codes are the contract: `0` the composition is valid, `1` the composition has problems and the complete report is still printed, `2` the project could not be read at all (diagnostics on stderr, no report). A harness that branches only on zero versus non-zero still behaves correctly; one that distinguishes `1` from `2` can tell "your project is wrong" from "I could not read your project".

The load waits up to 60 seconds by default, which is the right bound for a command whose entire value is a real answer. Streams and metadata blocks are byte-bounded, so a package with an enormous `metadata()` fails as an explained size refusal (`PACKAGE_METADATA_TOO_LARGE`) rather than as a mysterious hang.

No absolute path appears in the machine-readable report — not the repository root, not a temporary directory, not the invoking user's home — so two identical projects on different machines produce identical bytes and a diff always means the project changed.

Where it fits

It is step one of every agent workflow in this repository, and every shipped skill opens with the same byte-identical orientation block telling the agent to run it and read `valid`, then `problems[]`, then `limitations[]`, in that order.

Downstream, `crm solution check` binds a plan to this report's fingerprint, `crm project doctor` loads it exactly once and reuses it for every check, and `crm package test` asserts that a composed package agrees with what the inspector says about it. Those three are the reason the report is deterministic rather than merely readable.

The evidence this page rests on

Claims and limitations are printed from site/claims.json word for word. Job statuses come from docs/benchmarks/jobs.json; a job with no page of its own is listed with its status rather than linked.

Claims

  • C-14 One command tells an agent what an application actually is — packages, capabilities, resources, actions, policies, providers — read from checked-in source, in a single deterministic JSON report.

    LimitSource-only and read-only. It never opens the database, contacts a provider, reads a secret, or reports runtime, CI or authorization state — and it lists those blind spots as machine-readable limitations in its own output.

  • C-22 One command composes the whole thing and then inspects it: 70 modules, 6 packages, 41 resources, 56 actions, 7 policies and 5 providers, applied from manifests and driven end to end — then it prints the eleven things the inspector says it cannot see.

    LimitIt composes the starter's application, not yours, and it runs entirely locally against SQLite with no authentication. The counts describe what that starter applies; a different composition gives different numbers. Wall-clock time varies by machine and is deliberately not claimed.

  • C-17 Zero third-party runtime dependencies. Node 22 and a checkout — no build step, no bundler, no framework underneath your framework.

    LimitDevelopment dependencies and the eventual PostgreSQL adapter are separate questions. Having no runtime dependencies is a property of the framework, not of whatever you add on top of it.

Limitations

  • L-01 No authentication, tenancy or RBAC. The server is local-development-only. An actor header is an assertion, not an identity. Do not expose it to a network.
  • L-02 SQLite only. Persistence is Node's built-in SQLite adapter. PostgreSQL is on the Production Spine track and is not implemented.
  • L-06 No import, export, dedupe, merge, bulk edit, saved views or global search. Table stakes in every commercial CRM, and none of them has a milestone yet. This is recorded deliberately rather than left for you to discover.

Jobs it covers