Repository navigation
feat(releases): read and apply the AI-DLC 2.11 scope settings - #581
Merged
Merged
Conversation
eipasteur
requested review from
JWThewes,
jeromevdl,
leandrodamascena and
svozza
as code owners
October 18, 2026 19:12
…blocker An execution-relevant authored field no adapter consumes is how a release silently loses a setting. The closure already records the field's name, so the promotion guard now names it as a blocker instead of leaving it to certification, and a fork authoring the same field is held the same way. That changes what the evidence collector decides, so the fidelity evidence revision goes from 4 to 5: a list stored by the previous build carries a revision that looks complete while holding a verdict reached without this blocker, and promotion must re-read the immutable closure for such a record rather than trust it. The numeric-revision case is pinned alongside the no-revision one the registry already covered.
…ers a body `annex-reach.js` is a claim about the prompt BUILDERS: the promotion guard stops blocking an engine command found in a reaching block, so every session the runtime renders that block into has to receive the answer. The guard and the builders already share the module, but sharing a set is not delivering it — a builder can gate on a part the set does not name, or stop gating on one it does, and the existing cases assert each builder in isolation with hand-built parts. Walk the blocks of the real 2.9.0 catalog instead, and for each claimed block build the real prompt of each session that renders it: the lead stage prompt, the reviewer prompt `runReviewer` really builds, the support, link and integrator briefs from the shared persona tail, and the compose prompt. Pin that every session kind was exercised, and that the block types no builder gates on (RULE, SCOPE, ARTIFACT, SENSOR) stay outside the set. The role sets are read with the CATALOG block shape — `supportAgents` and a `reviewer` string, not the resolved plan's `supportAgentRefs` / `reviewer.reviewerAgent`. Reading the plan's names leaves both sets empty and silently reduces the whole check to lead sessions, so the enumeration is asserted non-empty with the dual-role overlap that makes the secondary sessions reachable. Behaviour is unchanged; this is a test.
`guard_policy`, `plan_approval`, `collaborators` and `existing_code` reach the SCOPE block, and the importer revision and pinned mapper fingerprint move with the mapper so a new import cannot be published under the old revision's prefix. An existing pin's closure is already-mapped bytes behind an immutable digest and is never re-mapped; `changeControl` reads `guard_policy` first and `change_control` second, so a 2.8.2 or 2.9.0 scope maps to the bytes it maps to today. Mapping a field removes its `unmapped` promotion blocker, and nothing else here makes the setting work, so each of the four is classified in the capability registry in the same change — every value `unsupported` with no handler, until the PR that resolves it onto the runtime names its seam. A fork authoring any of them therefore still holds promotion rather than running with a setting the platform drops; `change_control` gains the `off` value for the same reason, so a 2.11 scope's plan is rejected by its classification rather than by an enum it was never taught. `existing_code` is the one authored as a YAML boolean, so the registry declares the authored type and the analyzer compares the real parsed value. Upstream's `existing_code: true` imports and keeps its type; a quoted `"true"`, which the mapper would carry to a consumer comparing with `true`, is refused at import.
…on bump The golden digests of every published release are reproduced under their own recorded revision, so the bump can only change bodies imported from now on.
…boundary The enum check compared `String(value)`, so a singleton array passed every string-valued policy enum and a quoted boolean passed the project type. The comment assumed the import analyzer had already checked the parsed type, but a user SCOPE overlay never reaches that analyzer: the building-block API keeps intrinsic attributes verbatim and the release resolver reads the pinned row as saved, so the plan is that block's first and only judge. The registry already declares each entry's authored type for the analyzer, so carry it on the plan's policy rows too and check it before comparing the enum text. Consumers compare with `===`, so an accepted `['strict']` matched no change-control branch and an accepted `'true'` produced no project-type instruction - both now fail the plan closed instead.
Classifying a field as a scope policy also added its plan key to the predicate that decides whether a stage gets a resolved policy at all. The four 2.11 settings are deliberately classified `unsupported` until their handlers ship, yet a scope - or a user SCOPE overlay, which the promotion guard never sees - authoring only `collaborators` turned `policy: null` into a full policy. That switched on the unrelated defaults and the enablement gate the ensemble topology reads, so an overlay declaring collaborators off acquired a collaborator and a change-control ritual it never asked for. Split the registry's derived key lists: one names every policy field the plan KNOWS, so its type and value are still checked, and one names the subset whose handling is no longer `unsupported`. Validation reads the first, activation the second, so a field becomes able to change a run in the same change that gives it a consumer. `guard_policy` stays an activator because the mapper resolves change control from it, which has had a consumer since 2.6.18.
…tation Activation reads the FIELD's handling, so a field with a consumer switches a stage policy on for every one of its legal values. `change_control` has had a consumer since 2.6.18 and its third value `off` is classified `unsupported`, which left that value legal, activating, and uninterpreted: the plan carried `policy.changeControl: 'off'` and the runtime's change-control section entered on truthiness and fell through every implemented outcome, running the stage with no comparison at all. A release authoring the value is held by the promotion guard, but an immutable user SCOPE overlay is stored verbatim and never meets that guard. Carry the uninterpreted subset of each policy field's values on the same registry rows that carry its enum, and refuse such a value where the overlay enters execution: a user-authored block declaring one fails the plan with `policy_value_uninterpreted` instead of resolving a policy. A release closure's own scope is unchanged - it still imports, still plans, and is still held at promotion - so the complete 2.11 body remains importable and inspectable. The runtime asks the same registry question before acting. A change control it does not interpret now fails the stage with `change_control_uninterpreted` rather than proceeding as the absence of change control, which keeps the section's "three outcomes and no fourth" contract true for a value that reached a policy by another route.
…ype from the scope The three settings A3 carries onto the SCOPE block reach the per-stage policy, and their registry classifications move from `unsupported` to the seams that honour them, so a release authoring them stops holding promotion for them. `collaborators` keeps its blocker: its handler is not here. Guard Policy resolves ONE effective value, the new name first and the legacy `change_control` second, and only then translates `off` to no change control. An imported closure carries both names, but a user SCOPE overlay is stored verbatim and never passes through the mapper, so a forked scope may declare the new name alone or both disagreeing — resolving it in the plan is what keeps a caller from having to synchronize an internal alias. `off` means no reopen and no change-control prose, and nothing else. Plan Approval `off` withdraws `policy.planApproval` only; the stage's `humanValidation` is untouched for every scope and stage. The authored project type reaches `policy.existingCode`, and the stage prompt states the brownfield reading it implies, which is the seam the registry now names. Classifying these three also has to keep an `unmapped` gap refused, in the same change. Such a gap records that the importer of that closure stored no value for the field: the information is absent from the catalog, not merely unhandled by this build. Were it matched against the registry like an authored value, adding an entry here would clear an old closure's blocker, and a deployment could promote and run a revision-2 closure as though it carried the strict guard its source authored. The value is unconditionally unhonoured; clearing it requires re-importing the source under the newer revision, which is what actually recovers it.
`collaborators: off` dispatched the full support roster: the switch reached the SCOPE block and stopped there. It now reaches the per-stage policy, and the ensemble runner resolves NO topology for it — this runtime's existing single-session contract, where the lead produces every declared stage output itself. A shortened roster would instead leave some declared outputs unowned. The registry entry moves from `unsupported` to that seam in the same change, so the value stops holding promotion exactly when it starts working, and the one mode set both the runner's gate and the plan's record of the downgrade read is exported once. The stage's authored `mode` and `supportAgentRefs` stay on the plan: the filter is a policy effect, not a topology rewrite.
…raws A stage that runs with three fewer sessions than its block declares leaves a record naming each one, the way a removed sensor already does, so the downgrade reaches the timeline instead of being a silently shorter roster.
The withdrawal audit tested the mode set alone, while a subagent stage receives support sessions only when the closure ships the hub-and-spoke protocol file and its support refs resolve to AGENT blocks - conditions the topology resolver checks and the audit did not. A fork that imports and promotes cleanly without that protocol therefore had three personas named as withdrawn on a stage that was already a single session, and `runStage` serialized that claim into `v2.policy.applied`. Move the complete eligibility test into the plan module as `supportSessionRefs` and have both the topology resolver and the audit call it, so the two can no longer disagree about whether a session existed to withdraw.
eipasteur
force-pushed
the
feat/aidlc-211-scope-settings
branch
from
October 20, 2026 16:57
e441f55 to
2a8a42b
Compare
JWThewes
approved these changes
Oct 20, 2026
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.
In short
AI-DLC 2.11 gives each scope four settings: Guard Policy (whether a change to an already-approved document needs re-approval: strict, relaxed or off), Plan Approval (whether the code plan needs a human approval), Collaborators (whether support agents help the lead agent) and, for three scopes, project type (existing code). Today the platform drops all four when it imports a release, so a 2.11 run would ignore them:
enterprisewould lose its strict guard,expresswould still stop for a plan approval its authors turned off, and a scope asking for one agent would run several. The promotion check did not notice.This PR first makes the promotion check refuse any release that sets a field the platform cannot apply, then teaches the platform the four settings and applies them. 2.11 itself is still not promotable after this PR; that is #583.
Stacked on #521 (
fix/release-promotion).What changes
offremoves the re-approval, nothing else); Plan Approvaloffremoves the plan approval step; project type tells the agent it works on existing code. No stage loses a human validation gate, whatever the settings.Behaviour by release
Validation
expressintent ran without a plan approval step, and aclassicintent ran with it and without support agents.Limitations and follow-ups