Toolchains, architecture packs and indexers
Declare the language version and deterministic verifiers, bind architecture rules to checks, and choose the indexers that build the code graph.
The three kinds below are defined in the public schema and validate with the open numi validate --files. Resolving them into a profile, and running the verifiers they name, happens in the proprietary runtime, which is not generally available.
A LanguageToolchain says which language version a component uses and which deterministic verifiers judge a change. An ArchitecturePack ties each convention to a check that can fail. An IndexerPlugin names who builds the code graph. None of them tells a model what to do; they tell the platform what to measure.
When to use these kinds
Use them when a repository needs checks that do not depend on a model's opinion. Verifiers are the independent layer of verification and repair: the author agent proposes a change, and the toolchain's verifiers, not the author, decide whether it passes. An architecture rule is only enforceable if some verifier can report it, which is why the schema refuses a rule without a check.
All three share the standard envelope described in layout and scopes: apiVersion: numinance.dev/v1, kind, metadata.name, metadata.scope and an optional extensions map under reverse-DNS keys.
Where they apply
| Kind | Scopes in the public examples | Merge behaviour that the schema declares |
|---|---|---|
LanguageToolchain | SCOPE_ORG | formatters is appended: a narrower scope adds names to the wider scope's list |
ArchitecturePack | SCOPE_ORG | none declared; the pack is referenced by name from an SDLCProfile |
IndexerPlugin | SCOPE_REPO | none declared |
Only the kinds named orgOnly in the loader (KnowledgeConnection, ControlPack, ExecutorManifest) are refused below the org. The three kinds on this page accept any non-zero scope, and the SDLCProfile decides which names a component uses. Whether a repository may select a name the org has not granted is a profile-resolution question; the open build does not answer it.
LanguageToolchain
Prop
Type
apiVersion: numinance.dev/v1
kind: LanguageToolchain
metadata: {name: go-1.27, scope: SCOPE_ORG}
spec:
language: go
version: "1.27"
verifiers:
- {name: unit-tests, provider: dev.numinance.official/go-test, config: {race: true}}
- {name: lint, provider: dev.numinance.official/golangci-lint}
formatters: [gofmt]
package_manager: go-modulesA provider id such as dev.numinance.official/go-test is a reference, not an installation. In the designed runtime it must resolve to a signed, digest-pinned entry in the lockfile before it runs; the schema checks only that the string is well formed. See registry and trust.
ArchitecturePack
Prop
Type
apiVersion: numinance.dev/v1
kind: ArchitecturePack
metadata: {name: layered-service, scope: SCOPE_ORG}
spec:
rules:
- id: no-domain-io
statement: The domain layer performs no I/O.
severity: FINDING_SEVERITY_HIGH
check: {verifier: lint, rule_id: domain-imports, config: {forbidden: [net, os, database/sql]}}
- id: handlers-thin
statement: Transport handlers hold no business rules.
severity: FINDING_SEVERITY_MEDIUM
check: {verifier: lint, rule_id: transport-imports}check.verifier: lint names the lint verifier of the toolchain above. The open loader does not cross-check that reference; it is resolved when the profile is built.
An architecture rule is a convention that appears in two places. Its statement can reach a model as guidance inside a context bundle, labelled untrusted. Its check is the part that enforces. Enforcement never comes from the model having read the sentence.
IndexerPlugin
Prop
Type
apiVersion: numinance.dev/v1
kind: IndexerPlugin
metadata: {name: polyglot, scope: SCOPE_REPO}
spec:
indexers:
- provider: dev.numinance.official/scip-go
languages: [go]
edge_overlays: [http-routes]
- provider: dev.numinance.official/tree-sitter
languages: [python, typescript]
extensions:
com.example.indexing: {nightly: true}The extensions block shows the one place free-form data is allowed. Keys must be reverse-DNS, and the engine never reads them. Do not put credentials, endpoints or tool mappings there.
Security implications
- Names are references, not commands.
formatters,package_managerandproviderare resolved against declared providers and the lockfile. A string in these fields is never handed to a shell. configis opaque to the engine and is passed to the verifier. Treat it as the verifier's own contract and review it like code.- Verifier results are inputs to policy. A failing check lowers confidence and can block a stage; a passing check does not grant authority. Policy is evaluated outside the model before every privileged action.
- A verifier that runs candidate code (for example a unit-test runner) needs an isolated execution environment. The
numi initoutput notes that its generated Pythonunit-testsverifier is unavailable in the live verifier provider until a nested sandbox exists, so a verdict that depends on it comes out inconclusive, never as a pass.
Real failures
I edited copies of the example files and ran the open validator. Each error below is the verbatim output; the path in the command is a scratch copy.
$ numi validate --files bad/doc.yaml # "verifiers:" renamed to "verifier:"
invalid file "doc.yaml": kind: does not match the kind schema: schema: value does not match (violations: 1): \"/spec/verifier\": additionalProperties
(exit 3)
$ numi validate --files bad/doc.yaml # "verifiers: []"
invalid file "doc.yaml": kind: invalid: numinance.kinds.v1.LanguageToolchainSpec.verifiers is empty
(exit 3)
$ numi validate --files bad/doc.yaml # a rule with no "check"
invalid file "doc.yaml": kind: invalid: numinance.kinds.v1.ArchitectureRule.check is empty
(exit 3)
$ numi validate --files bad/doc.yaml # severity: HIGH instead of FINDING_SEVERITY_HIGH
invalid file "doc.yaml": kind: does not match the kind schema: schema: value does not match (violations: 1): \"/spec/rules/0/severity\": enum
(exit 3)
$ numi validate --files bad/doc.yaml # extensions key "indexing" instead of "com.example.indexing"
invalid file "doc.yaml": kind: invalid: a key of numinance.kinds.v1.IndexerPlugin.extensions is not reverse-DNS
(exit 3)Enums are always the full proto name. HIGH is not accepted; FINDING_SEVERITY_HIGH is.
Next steps
StageRegistry
Map your own stage names and gates onto the fixed Change state machine, with human approval, separation of duties and timeouts.
Adapters, environments and executors
Bind ports to providers with an AdapterSelector, describe disposable environments with an EnvironmentProvider, and declare an author executor with an ExecutorManifest.