docs(cdrus): RFC for CDrus Expressions and Workflow schema - #326
docs(cdrus): RFC for CDrus Expressions and Workflow schema#326sol-duara wants to merge 1 commit into
Conversation
Adds a draft RFC and two JSON Schemas proposing a declarative grammar for SDLC workflow intent on top of CDEvents, in response to the Workflow Segments proposal in cdevents#253. CDEvents standardizes the vocabulary of what happened. cdevents#253 identified the missing layer: no shared structure for what was intended. This RFC proposes two artifacts for that layer. CDrus Expressions are identity-bound, composable units of intent that declare which CDEvents fulfill them. Each is addressed by the tuple (group, author, expression), which replaces implicit versioning with ownership plus a stable addressing key. CDrus Workflows are concrete documents that reference Expressions by identity and bind them to tools, sources and per-event content. The separation is deliberate: Expressions stay tool-agnostic, Workflows carry tool binding. That answers the "split tools from workflow" suggestion in the cdevents#253 thread, and the observation that producers cannot be made compliant with segments while consumers can reason in their terms - compliance lives on the consumer side, binding in the Workflow, intent in the Expression. Beyond cdevents#253 the RFC contributes depth-first nested production, which expresses causal structure within a chain, and spawned chains in two forms: Blocking (spawn, awaited) and Detached (detach, fire-and-forget), each on its own chainId with a RELATION link of kind TRIGGER back to the spawning event. Both build on CDEvents primitives that already exist. The schemas use JSON Schema 2020-12, matching the dialect of the CDEvents event schemas, and live in a self-contained cdrus/ directory so they are not picked up by the schemas/ event-schema glob. Placement remains the open governance question raised in cdevents#253 and is deferred to the maintainers. Refs cdevents#253 Signed-off-by: Dadisi Sanyika <dadisi@solduara.com>
|
Thanks for the depth here. The verification section alone (11/12 examples validating against claimed schemas, 54/54 existing schemas unregressed) is more rigor than most spec PRs get, and the identity model (unversioned tuple + asymmetric add/remove rules on produces) is a clean way to avoid version churn. Why I'm asking for a split: CDEvents' stated purpose is a shared vocabulary that stays agnostic about how something happened, so any tool can emit or consume it, "the flexibility to incorporate whatever tooling best suits your specific requirements," per the project's own framing. expression.schema.json stays inside that: it composes and names CDEvents that already exist (subjects, chainId, RELATION/TRIGGER via spawn/detach), without saying who emits them or how long to wait. workflow.schema.json crosses that line: tool, pipeline, timeout_ms, min_wait_ms bind intent to a specific runner with specific timing, that's orchestration-engine configuration, not shared vocabulary. The RFC's own 8.2 draws this same distinction (compliance and binding live on the consumer side, not the producer/vocabulary side); I'm applying that line to where the files themselves should live, not just to producer behavior. My suggestions:
The rule, so it's checkable rather than a matter of opinion: anything that tells a consumer which physical tool or pipeline must emit or is bound to an event, or how long to wait for it, is out of scope for this repo. Anything that names or composes CDEvents that already exist, without specifying who emits them or how long to wait, is in scope. Applying it: group, author, expression, produces, spawn, detach (in expression.schema.json) bind to none of that. in scope, unchanged. tool, source, pipeline, timeout_ms, min_wait_ms, defaults, overrides (in workflow.schema.json) bind to all of it, out of scope here. Not part of this ask: spawn/detach are defined identically in both files. I'm not asking you to remove or change them anywhere, they stay exactly as written, in expression.schema.json. I would love for @afrittoli to chime in, but my first thought is this should be split. The RFC's own "Placement" section already raises new-repo-vs-subspec as an open question for workflow.schema.json, a new repository under the CDEvents org, or Sol Duara, Inc.'s own repo, both work; cdevents/spec does not. @afrittoli - thoughts? |
|
Thanks @sol-duara for this submission and @xibz for your comment. |
Changes
Adds a draft RFC and two JSON Schemas defining a declarative grammar for SDLC workflow intent on top of CDEvents, in response to #253 (Enable CI/CD Interoperability Through SDLC Workflow Segments).
cdrus/README.mdcdrus/expression.schema.jsoncdrus/workflow.schema.jsonThis is submitted for comment rather than as a finished specification, per §1 of the RFC.
The problem, and how this addresses it
#253 stated it precisely. CDEvents standardizes the vocabulary of what happened; no shared structure exists for what was intended. Without that layer, orchestrators and policy engines encode workflow semantics imperatively, in code or in tool-specific configuration, which is the coupling CDEvents otherwise removes.
#253 asked for three things. All three are addressed:
Propagate what each tool's role in the workflow is, not just what it does. A CDrus Expression declares which CDEvents fulfill an intent such as
build,deploy, orverify, independent of any tool. The declaration is the role.Validate that a tool fulfilled its intended role. An Expression resolves to a determinate tree of expected events (§6.2), so the expectation is expressed in a form that can be checked. Which checking process a consumer runs against it, and how strictly, is the consumer's choice. The RFC is on the emitter side of that boundary: it delivers the verifiable declaration, not a mandated verification procedure.
Port workflows between systems without rewriting automation. Tool binding lives in the Workflow layer, never in the Expression. Swapping Jenkins for Tekton in a pipeline changes one line of one Workflow, and the Expression graph is untouched.
The three questions raised in the #253 thread
Naming and tool binding, raised by
@thompson-tomo. The suggestion was to split tool definitions from workflow definitions. The RFC realizes that intuition as a layer split rather than a document split: Expressions carry intent and name no tool, while Workflows carrytool,source,pipeline, and per-eventoverrides(§5.5). Theoverridesmap is what lets a single composite bindbuild.*to one tool anddeployment.*to another without touching the Expression.Producer versus consumer compliance, raised by
@afrittoli. The observation was that tools producing events cannot always be made compliant, because pipeline definitions are end-user defined, while tools consuming events can reason in segment terms. The RFC adopts that distinction as its architecture (§8.2). Producers emit whatever their native pipeline yields. The Workflow binds a producer's tool identity to each event. Compliance lives on the consumer side, binding lives in the Workflow, and intent lives in the Expression. Requiring producer-side compliance would have demanded behavioral change from every CI/CD tool, which would defeat the goal of #253.Hosting and governance, raised by
@afrittoli. Deferred to the maintainers. See Placement below.Deviations from #253, and the case for each
segmentbecomesexpression. The RFC gives three reasons (§4.2). A segment implies a piece of a larger pre-existing whole, while the construct here is generative and evaluates to a sequence of events. Segments also do not compose with segments without strain, whereas expressions compose with expressions, and the schema relies on that composition throughout. Migration is a keyword substitution plus the identity tuple: §4.2 preserves the semantic content of the original proposal intact.Identity replaces implicit versioning. Expressions carry no version. They are addressed by the tuple
(group, author, expression), and the change rules are asymmetric (§4.4): adding events toproducesnever changes identity, while removing events changes identity only when the removal leaves a name hint unsatisfied. AbuildExpression therefore always contains whatbuildpromises, without a version bump every time a step is added.Identity is a consistent addressing key rather than a verified credential (§4.4, §9.2). All resolution requires is that the tuple be consistent and unique. Whether
groupandauthorcorrespond to real principals is not the language's concern, which is what lets a deployment back the pair with whatever it already has: native RBAC, enterprise Active Directory or LDAP, or a stub for testing. Who may publish under an identity is governance the deployment provides, and the language neither enforces nor depends on it.required_fieldsmoves to the CDEvent schemas. #253 carried per-segment required fields such asbuild_system,change_hash, andoutcome. The RFC places field requirements on the CDEvent schemas instead (§8.1), so a field is specified once, in the schema that defines the event, rather than in two places that can disagree.Name hints constrain the declaration. CDEvent subjects are reserved keywords in the expression token (§4.1.1). A subject appearing as a complete token requires the corresponding events in
produces: both the beginning and ending event for a subject with a clear begin and end pair, or at least one event otherwise. Hints are a floor and not a ceiling, so a hinted Expression may declare more than its name mentions. This is what makes the asymmetric identity rules above coherent.Mechanisms added beyond #253
Depth-first nested production (§4.6). A nested
producesdeclares that children were caused by the parent, not merely that they follow it in time. All nested events share the parent'schainId.Spawned chains in two forms (§4.7, §4.8).
spawndeclares Blocking chains that the spawning chain waits on.detachdeclares Detached chains that it does not. Each spawned chain receives its ownchainIdand aRELATIONlink of kindTRIGGERback to the spawning event. The two are structurally symmetric and differ in a single bit, whether the spawning chain's completion is gated on them.Both are built from CDEvents primitives that already exist, so no core change is required. Together they express two cases #253 could not: parallel work that must complete before the parent finishes, such as two test cases under one suite, and downstream work that must be declared so consumers can track it but must not gate completion, such as a notification pipeline. A downstream chain that is never declared is tied to its parent by no declared relation and claimed by no expectation.
A Workflow layer with defaults and per-event overrides (§5). Workflow-wide
defaultsremove repetition, expression-level bindings apply to a whole resolved Expression, andoverridesbind individual event types.Named event anchors (§4.9). An optional
as:label on an event is pure intent metadata, with no effect on production, ordering, versioning, or chain assignment. A reference resolves to the list of every event carrying that anchor, so the behavior is uniform whether an anchor names one event or several.Scope
The RFC is explicit about its own boundaries. Execution is orthogonal to it (§2.3): Workflows supply the bindings a downstream engine needs, and how that engine works is not specified here. The Expression store is out of scope as an implementation choice (§4.4). How extended event types are created is out of scope (§6.1). Tool discovery is not in scope (§8.3). Who may publish under an identity, and the integrity of stored content, are deployment governance rather than language requirements (§9.2).
Placement
@afrittoliasked whether this belongs in a new repository under the CDEvents org or as a subspec ofspec. That decision is left to the maintainers.For this PR the files sit in a self-contained
cdrus/directory, mirroring howcustom/holds the custom-events subspec. The directory was chosen so that nothing else moves.tools/validate.jsglobsschemas/*.jsonand treats every match as an event schema, so placing these files there would have added them to the event-schema count and compiled them as events. Undercdrus/they are inert to the existing tooling, and the whole directory can be relocated, or lifted into its own repository, without changing a line of content.Schema
$idvalues usecdrus.devas an example namespace. Final assignment is a governance decision, and reassignment is a non-breaking structural change (§10.2).Verification
Ajv2020withvalidateFormats: true.<placeholder>values and is illustrative.produces,spawn, anddetach;spawnanddetacheach accept the flat and nested forms and reject the two mixed; the top level rejects a bare spawned chain;as:and identity tokens enforce their charset.tools/validate.jsreports 54/54 schemas and 50/50 examples, identical to this branch's baseline before the change.tools/validate.jsbuilds anAjv2020instance, which carries no draft-07 metaschema and fails to compile a draft-07 document. The conversion was verified to preserve every enforcement listed above.Submitter Checklist
As the author of this PR, please check off the items in this checklist:
cdrus/; no existing event schema is modified