Rulebearing Playground Coverage Schemas GitHub

ADR-0078: fleet cruises each repository as it is and evaluates the union; declared edges attach there, and an OpenAPI operation declares an edge only through the handler its document records

Context

Plan 0004 freezes the two file formats and two of the decisions: the union prefixes each module path with the repository's name and /, leaving module identity inside a repository unchanged so its rules and cache are reused as they are; and an OpenAPI edge is read only from a client folder edges.yaml names, matching operation ids to the server's endpoint handlers by the operation id the server's own document records, with no edge and a reason in the receipt when the document has no operation ids. It leaves open where declared edges attach, what fields a declared dependency carries beyond declared and dependencyKind, what a per-repository finding becomes in the fleet's report, what the fleet rules may contain, and, for OpenAPI, how a document records a handler and what counts as a client calling an operation. Each of these touches the "never inferred" line of ADR-0014 or the field contract of ADR-0004, so they are recorded here rather than left in code.

Decision

  1. Declared edges attach in fleet only. fleet reads edges.yaml beside the manifest, or the file --edges names, and adds its edges to the union before the fleet rules are evaluated. cruise does not read edges.yaml: its cache keys do not cover the file or the documents it names, and a mixed-language repository is a fleet of one (repos: [{ name: app, path: . }]). Paths in edges.yaml are the union's module paths, prefixed (web/src/api/client.ts).
  2. A declared edge joins two modules of the graph. An entry whose from or to is not a module of the union is not added; it is listed in summary.declared.skipped with the reason and printed as a warning. config lint --fleet reports a from, to or OpenAPI server that no longer exists as stale-declared-edge: against the modules of --graph when given, else against the repositories' files.
  3. The fields of a declared dependency. declared: true, dependencyKind: declared (a new value of the vocabulary), declaredKind (the entry's kind; http for an OpenAPI edge), comment (the entry's comment; for an OpenAPI edge the operation id, method, path and document), module (the to path; the operation id for an OpenAPI edge), no dependencyTypes, followable: false, and the moduleSystem of the from module's language (es6 for TypeScript and JavaScript, clr for .NET, py for Python), since the upstream field is required and has no value for an edge no import formed. declaredKind and comment are additive, absent on every detected edge, and --strict-schema strips them with declared and dependencyKind, as it strips sidecar from a sidecar edge: the edge stays and validates (plan § 1.8), and the strict document says only what dependency-cruiser's schema can say.
  4. An OpenAPI operation declares an edge only through what its documents record. The server document (OpenAPI 3 or Swagger 2, JSON or YAML) records a handler as the operation's x-rulebearing-handler extension: a file path relative to the repository the document is in, so src/Web/Endpoints/Orders.cs in api/openapi.json becomes api/src/Web/Endpoints/Orders.cs. A client module calls an operation when it is under the named client folder and the code layer its extractor wrote records a function, class or member declared in it named exactly as the operation id (a generated client declares one per operation; no case folding, no renaming a generator might do). The client side is read from the extractor's output, never from source text, so no source file is read outside an extractor (ADR-0010); the server document is read as a declaration, as edges.yaml is. Each such module declares one edge to the handler. An operation without an operationId, or without a handler, or that no client module declares, declares nothing; summary.declared.openapiSources[] counts each case per pair and, when a pair declares nothing, says why. A path item given by $ref is not followed.
  5. Each repository is cruised as it is. In its own folder, with the configuration the manifest names (or the one cruise would find), -T json, and its own cache: the configuration's options.cache when it sets one, else --cache at the configuration's default folder; fleet --no-cache turns it off for every repository. Repositories run in parallel on a pool as wide as the machine's parallelism. A repository whose folder is missing, or whose own run exits 2 or 3, stops the fleet with that exit code and the repository named.
  6. The union. Every module path, dependency resolved, and cycle and via entry, and the from and to of every violation over modules or folders, is prefixed with <name>/ (a project, element or slice violation names projects or types, and keeps its names), except a core module and an unresolved specifier, which are not paths of the repository and are merged into one module when several repositories reach them. The code layer is not carried: its type names are not paths and may repeat across repositories.
  7. The fleet rules are dependency rules. The manifest's rules take dependencies, layers and independence in the native format and are evaluated by rb-rules unchanged, with liveness strict. elements, slices, diagrams and ratchets are refused (exit 3): the first three read the code layer, and a ratchet's budget belongs to a repository.
  8. One report. Each repository's own findings are folded into the evaluated union, with their paths prefixed and their stable ids recomputed from the prefixed paths (ADR-0015), together with their counts, expired entries, ratchets and vacuous rules, and their rules are added to summary.ruleSetUsed, where a reporter finds each finding's comment and fix (the fleet's allowedSeverity stays the fleet's). The document is then reported by fmt's path, so every reporter is available and the exit code is computed from the document alone, as ADR-0031 does: a gating reporter exits with the fleet's and the repositories' error count. summary.fleet records each repository's own counts, receipt and cache verdict; summary.declared records the declared edges by source.

Consequences

Alternatives considered