Skip to content

feat(gitops): a render refuses what the cell's admission would, and a publish holds a tree to its declaration - #1290

Merged
antoinetoussaint-byte merged 2 commits into
mainfrom
fix/cell-judges-rendered-pods
Nov 4, 2026
Merged

antoinetoussaint-byte merged 2 commits into
mainfrom
fix/cell-judges-rendered-pods

Conversation

@antoinetoussaint-byte

Copy link
Copy Markdown
Contributor

Three escapes, one seam

Three defects reached a cell (obin-ai/platform-obin, env blue) through every gate on 2026-11-03. Each was visible from the rendered tree alone; nothing in the render path looked.

escape gate that should have refused why it did not fix here
a service's agent rendered livenessProbe.exec; the cell's execution-admission policy refuses an exec hook/probe, so the pod ran 0 replicas for a day deploy gitops render (the cell derivation is what generates that policy) renderedContainer read only name and image; no rule of the policy was mirrored in the render path refuseUnadmittedExecution: the two policy rules that read only the pod template (exec hooks/probes; mesh opt-out labels/annotations), refused by container and field
a withdrawn solution's tree and a never-declared module's tree stayed under the environment's modules dir; the cell file approved the one and the cell refused the other's pods while the delivery still ran them deploy gitops render deriveCell described every tree for the environment, whatever the workspace composes; only the converse (refuseUnrenderedModules) existed refuseUncomposedRender: a tree of a module the workspace does not compose refuses the cell file and names the tree for removal; rendered trees and composed modules are now the same set
a commit moved a service's service-config/service-identity rows after its tree was rendered; nothing compared the tree with the declaration it came from deploy gitops publish (as the workspace-configuration-group hold already does) the inventory recorded group digests but nothing of the environment's own rows environmentDeclarationDigest stamped in the inventory (Target renders), refuseStaleDeclaration at publish; services read from the inventory at both ends so a tree is never refused for a service the publish could not see; an unstamped tree is held to nothing

Tests (fail before, pass after)

  • TestCellFileRefusesAnExecProbeTheCellWouldRefuse (liveness/startup/postStart exec refused; an httpGet probe admitted)
  • TestCellFileRefusesAPodThatOptsOutOfTheMesh
  • TestCellFileRefusesATreeOfAModuleTheWorkspaceDoesNotCompose
  • TestTheDeclarationDigestFollowsTheModulesOwnRows, TestAPublishRefusesATreeRenderedBeforeTheDeclarationMoved
  • the byte-identical fixture digest moves with the new inventory field (both cases still agree), as documented in its comment.

go test ./pkg/gitops/ passes locally.

What this does to a cell carrying the defects today

The next render --all with this CLI refuses blue until (1) the parse agent's exec probe is an HTTP/TCP/gRPC probe (obin-ai/service-parse templates/deployment/kustomize/base/deployment.yaml.tmpl:81) and (2) the proposals and compute trees are removed from the environment's modules directory. That is the gate working; both refusals name the exact tree/container.

Classification: fixes at the owner of the render path. Not done here: the probe itself (service-parse), the tree removals (platform-obin), and a doctor --env report of the declaration stamp (the publish hold is the gate; doctor is a follow-up).

🤖 Generated with Claude Code

antoinetoussaint-byte and others added 2 commits November 3, 2026 23:33
… publish holds a tree to its declaration

Three defects reached a cell through every gate in one day. Each was
visible from the rendered tree alone, and nothing in the render path looked.

1. An exec probe. A service's agent rendered `livenessProbe.exec`; the cell's
   execution-admission policy refuses a pod with an exec hook or probe
   ("a command of the delivering party's choosing inside the pod that already
   holds the workload identity"), so the workload ran with zero replicas for a
   day while render, doctor and the promotion's checks were all green. The
   cell derivation now refuses such a workload by container and field
   (refuseUnadmittedExecution), and a pod template opting out of the mesh
   with it: the two rules of that policy that read only the pod template.
   The cell file this render writes is what generates the policy, so a
   workload it would describe and the policy would refuse is refused here.

2. A tree of a module the workspace no longer composes. A solution withdrawn
   from the composition kept its rendered tree, and a module nothing pinned
   kept one from an earlier composition. deriveCell walked the modules
   directory and described every tree for the environment, so the cell file
   approved what the composition had withdrawn; once a cell file was derived
   without one, the cell refused that tree's pods while the delivery still
   ran them. refuseUncomposedRender names the tree for removal; with
   refuseUnrenderedModules the rendered trees and the composed modules are
   now the same set.

3. A tree rendered before the environment's declaration changed. A commit
   moved a service's service-config and service-identity rows and nothing
   compared the rendered tree with the declaration it came from, so the
   delivery branch ran the pod on the value before the change. The inventory
   now records a digest of those rows for the module's services
   (environmentDeclarationDigest, beside the workspace configuration group
   digests), and publish refuses a tree whose stamp is not the environment's
   current one, naming the module and the render that lifts it. The services
   are read from the inventory at both ends, so a tree is never refused for a
   service the publish could not see; a tree with no stamp is held to nothing.

The byte-identical fixture digest moves with the new inventory field, as it
has for every inventory change before it; both cases still agree.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Review found the mesh opt-out check a false pass for CronJobs: the template
metadata was read at spec.template, and a CronJob keeps its pod template under
spec.jobTemplate.spec.template, so an opt-out label there passed the render
and would be refused by the cell at run time. The metadata is read through
podTemplate, as the pod spec already was, and the opt-out test adds a CronJob.
Also drops a stray context import from the declaration test and states that
solution units are outside the declaration digest.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@antoinetoussaint-byte
antoinetoussaint-byte added this pull request to the merge queue Nov 4, 2026
Merged via the queue into main with commit f7a7593 Nov 4, 2026
16 of 19 checks passed
antoinetoussaint-byte added a commit that referenced this pull request Nov 4, 2026
#1290 made the derivation refuse a tree of a module the workspace does not
compose and ask for its removal. The prune now runs before the derivation,
so a render satisfies that hold by removing the tree rather than skipping
it; the hold stays for any derivation nothing pruned first, and its test
asserts it there.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
antoinetoussaint-byte added a commit that referenced this pull request Nov 4, 2026
#1290 made the derivation refuse a tree of a module the workspace does not
compose and ask for its removal. The prune now runs before the derivation,
so a render satisfies that hold by removing the tree rather than skipping
it; the hold stays for any derivation nothing pruned first, and its test
asserts it there.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant