docs: chapter 16, dependencies and the derivation map - #59
Merged
Conversation
ExtraToast
force-pushed
the
docs/ch16-derivation-map
branch
2 times, most recently
from
August 31, 2026 11:19
5b39976 to
1a4f473
Compare
This was referenced Aug 31, 2026
Defines what declarations produce, and gives the specification its one machine-checkable property. The dependency edge is three fields. Four things derive from it read outbound: reconcile ordering, dependency coordinates, network policy egress, and co-test membership. A fifth class derives from the same edges read inbound, and those are the derivations a service can never declare locally because no service knows its own consumers: - one database and owning user per consumer, which is all init-databases.sh does in 98 lines - NetworkPolicy ingress, since only the inbound set says who to admit - co-test suite membership, which is why 12 of ~25 test classes exercise auth-api with its consumers rather than alone - browser origin allow-lists, which auth-api hand-maintains as nine hostnames - rotation blast radius, which is the inbound set of a secret path Includes the derivation map: 50 nodes, 65 edges, declared on the left and derived on the right. It is dense deliberately and is not meant to be read by eye -- its value is that three properties are checkable by a script over the attribution table ADR-0016 already requires. Corrects a criterion this specification previously stated wrongly. The overview claimed "any node with two inbound arrows is a bled concern". That is false: a Deployment legitimately draws on image, config, claims, health and placement. Convergence on an object is normal; convergence on the same field of an object is the defect. The three properties are now stated properly: 1. totality -- no deliverable has in-degree zero 2. single authority -- no field has two declaring sites 3. no dead declarations -- no declaration has out-degree zero Property 3 is the one that catches ceremony, and it would have caught two live examples: rollbackTargetRetention, validated and acknowledged by every service and read by no renderer; and platform.layer, which fed a registry and never placement while being wrong in 7 of 7 services. Two worked traces: one exposure declaration producing six artefacts plus retiring the two conformance tests that existed only to detect when those six disagreed; and one credential claim producing nine, the ninth computable only over the composed union. Leaves one question open rather than guessing it. The CORS derivation shape is right but the predicate is not established: an OIDC redirect flow needs no CORS entry, so it is probably inbound edges declaring a browser surface rather than all inbound edges. Recorded in the chapter to confirm before implementing.
The rewrite changed two nodes on the map and one worked trace. config becomes env files (base plus cluster overlay), and gains a second outbound edge: a secret placeholder resolves to an envFrom secretRef rather than a literal env entry, so the env file now feeds both the Deployment and the Secret. claims becomes secrets.yml grants, carrying access and delivery. Its derivations are unchanged in substance -- policy, auth role, secret, network policy, container wiring -- but the declaration site moved out of the Service document. health becomes readiness plus liveness as siblings. The credential trace is rewritten to the new two-file shape and gains the bidirectional check the single-document form could not express: a delivery: env grant with no placeholder is a dead grant, a placeholder with no grant is an unauthorised reference, delivery: env with tolerates: reload is impossible, and self-roll on a path other services read needs acknowledgement. That last check is the one nothing in the estate has today. secret/platform/observability holds the Prometheus token, the Discord webhook and the Grafana client secret, and one CronJob rolls one of those keys. Adds a row to the caught-defects table: a secret granted but never referenced is property 3 again, a declaration with out-degree zero. Revalidated: all three mermaid blocks resolve every edge endpoint, and the map still passes its own property 3 -- every declaration has at least one outbound edge.
ExtraToast
force-pushed
the
docs/ch16-derivation-map
branch
from
August 31, 2026 13:37
1a4f473 to
7bd8c55
Compare
ExtraToast
changed the base branch from
docs/ch10-service-intent
to
v1-pre-release
August 31, 2026 13:37
ExtraToast
added a commit
that referenced
this pull request
Aug 31, 2026
Lands the remainder of the v1 specification. Chapters 10 and 16 merged as #58 and #59; this carries 20, 30, 40, 50 and 60 together, plus the revisions those chapters make to earlier ADRs and the examples. Combined rather than stacked, because the stack could not be salvaged after the first two squash-merges: each branch was cut from the pre-squash tip of the one below, so rebasing them onto the merged base conflicted. Splitting them again after the fact would also have produced PRs that do not stand alone -- chapters 50 and 60 revise 30 and 40 (prune semantics, class C's home, two resolved open items), and the glossary correction touches all of them. #60 through #63 remain as the per-chapter review record. Chapter 20, the Resolved Deployment. One rule carries the specification: every assignment is a pure function of Service Intent, the pinned Cluster Context and the pinned locks. Nothing is allocated or remembered between renders. Applying it exposed a hole in ADR-0003 -- taken literally it forbids declaring the Service Id -- so identity is separated from pool: identity is declared and checked, a pool is assigned. Not one live hostname derives from a Service Id, so an exposure carries a name. Also corrects something said repeatedly and wrongly: layer 2 assigns a selector and an affinity, never a node. Chapter 30, the Deliverable Set. Closes the coverage question with a measurement: 450 objects, 39 kinds, 329 files, split 364 intent-derived, 41 pack-delivered, 45 authored. 328 of the 364 have an adapter; the 36-object gap is 14 that need only registering and 22 that need writing. Chapter 40, composition. 26 estate-wide invariants across identity, references, secrets and completeness. The unit of publication is a repository rather than a domain, which resolves the homelab-collections split question without a decision. Order-independence forces every collision to be an error rather than a last-write-wins merge. Chapter 50, lifecycle, with ADR-0019. Push delivery gated by relationship-scoped aggregators; Flux keeps the foundation, because 18 of the 41 pack-delivered objects are HelmRelease and applying one with kubectl does nothing without helm-controller. The cluster is the inventory: a deployer label supplies what Flux gets from a Kustomization's record. Drift correction is an in-cluster CronJob, not an Actions schedule, because the estate measured Actions crons at four to seven runs per day regardless of the declared interval. Chapter 60, setup and adoption. The ordering hazard is the point: Flux prunes, so spec.prune must be set false BEFORE a Service's manifests leave the Flux source, or adoption deletes it. Also: the glossary was materially stale and is corrected -- Claim Mode described a model that no longer exists, Contended Value conflated identity with pool, and seven terms were missing. 39 terms to 47. Four workflows, an Aggregator declaration, a Renovate custom manager, the generated deployer RBAC, the reapply CronJob and a negative fixture. Every workflow is one job with many steps, because jobs bill by the minute rounded up. Audit at commit time: 11 mermaid blocks valid across three diagram types, 18 example files parsing, no broken relative links, no stale vocabulary.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Chapter 16. Stacked on #58 — review that one first; this PR's base retargets to
v1-pre-releaseautomatically when #58 merges.Chapter 10 defined what a Service declares. This chapter defines what those declarations produce, and gives the specification its one machine-checkable property.
Direction is the interesting part
Four things derive from a dependency edge read outbound: reconcile ordering, dependency coordinates, NetworkPolicy egress, co-test membership.
A fifth class derives from the same edges read inbound, and these are the derivations a Service can never declare locally, because no Service knows its own consumers:
init-databases.shcreatesauth_db,agents_db,knowledge_db,n8n_db— one per Service claiming a Postgres credential. That is all 98 lines do.auth-apiwith its consumersauth-apihand-maintains nine hostnames inAUTH_CORS_ALLOWED_ORIGINSThis is why ADR-0014 puts co-test declarations on the test project, and why ADR-0015 is a hard dependency of this chapter — an inbound derivation is only computable over the composed union.
The map
50 nodes, 65 edges. Dense on purpose, and not meant to be read by eye — its value is that three properties are checkable by a script over the attribution table ADR-0016 already requires every Deliverable to carry.
This PR corrects a criterion the spec previously stated wrongly
00-overview.mdclaimed the acceptance criterion was "any node with two inbound arrows is a bled concern." That is false. ADeploymentlegitimately draws onimage,config,claims,healthandplacement. Convergence on an object is normal; convergence on the same field of an object is the defect. Superseded by three properties stated properly:kb.jorisjonkers.devviolated seven ways over.Property 3 is the one that catches ceremony, and it would have caught two live examples:
rollbackTargetRetention, validated and acknowledged identically by every service and read by no renderer; andplatform.layer, which fed a registry and never placement while being wrong in 7 of 7 services.Two worked traces
One exposure declaration → six artefacts, plus retiring the two conformance tests (
route-auth-conformance.test.js,gatus-route-coverage.test.js) that existed only to detect when those six disagreed. Under property 1 they have nothing left to check, because the six share a single upstream.One credential claim → nine derivations, the ninth being the provider-side database and user, computable only over the composed union.
Left open rather than guessed
The CORS derivation shape is right but the predicate is not established. An OIDC redirect flow — which
GrafanaOidc,N8nOidcandRabbitMqOidcexercise — needs no CORS entry, so it is probably "inbound edges declaring a browser surface" rather than "all inbound edges". Recorded in the chapter to confirm againstauth-api's actual CORS usage before implementing.