Open
Conversation
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
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.
What this does
Today, creating or updating a control requires hand-editing the full control JSON — condition trees, evaluator configs, selector paths, and all. Templates let callers define a reusable control shape with named, typed parameters, and then create or update controls by filling in just the parameter values.
For example, instead of constructing an entire regex-denial control from scratch, a caller can submit a template with a
patternparameter and astep_nameparameter, provide values for those parameters, and get a fully rendered, validated control back.RFC and implementation plan: https://gist.github.com/lan17/ea9aaca990c9bcbfda6595469f3e76c5
How it works
Templates use a render-before-save design. The caller sends a
TemplateDefinition(parameter schema + adefinition_templatewith{"$param": "..."}placeholders) andtemplate_values. The server substitutes the values, validates the result as an ordinaryControlDefinition, and stores both the rendered control and the template metadata in the samecontrols.dataJSONB column. No schema migration needed.The evaluation engine never sees template metadata. It reads controls through a slim
ControlDefinitionRuntimemodel withextra="ignore", so template fields in the JSONB are skipped at zero cost. From the engine's perspective, a template-backed control is just a control.Key design decisions:
PUT /controls) and update (PUT /controls/{id}/data) endpoints detect template payloads via aControlDefinition | TemplateControlInputunion and render transparently. One new endpoint (POST /control-templates/render) provides stateless previews.enabledandnamestay outside the template. Templates cannot set or bind these fields.enabledis managed viaPATCHand preserved across template updates. This prevents a template update from silently re-enabling a disabled control.PUT /datawith a rawControlDefinitionon a template-backed control returns 409 to prevent accidental template stripping by older clients.$parambinding, so the caller sees"field": "template_values.pattern"instead of"field": "condition.evaluator.config.pattern".Reviewer guide
Start here — these three tests show the full lifecycle:
test_render_control_template_preview_returns_rendered_control— preview a template without persistingtest_create_template_backed_control_persists_template_metadata— create and verify stored statetest_template_backed_control_evaluates_after_policy_attachment— attach to agent and verify evaluation behaviorThen follow by layer:
models/.../controls.py_ConditionBackedControlMixin,ControlDefinitionextension (template/template_values + invariant validator),ControlDefinitionRuntimemodels/.../server.py_parse_control_input— discriminates raw vs template payloads, rejects mixed payloads with a clear errorserver/.../services/control_templates.py$paramsubstitution, reverse path map, error remapping, agent-scoped evaluator rejectionserver/.../endpoints/controls.pytemplate_backedlist filterserver/.../services/controls.py,engine/.../core.pyControlDefinitionRuntimewired into evaluation hot path,ControlDefinitionLikeprotocolsdks/python/.../controls.pyto_template_control_input()reshape helper, template-awarecreate/update/validate/renderV1 limitations
:before the existing validator can attempt to load an agent$paramescaping — the$paramkey is reserved in all template JSON values$paramreplaces the entire JSON value, not a substringValidation
make checkmake sdk-ts-generatemake sdk-ts-name-checkmake sdk-ts-typecheckmake sdk-ts-build