@awak-app/simy-cli 0.5.12 → 0.5.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@awak-app/simy-cli",
3
- "version": "0.5.12",
3
+ "version": "0.5.13",
4
4
  "description": "Local SIMY executor for non-coding Local Tasks and Agentic Loop runs.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -13,7 +13,7 @@ import {
13
13
  } from "./durable-local-task-steps-contract.js";
14
14
  import { buildExecutionCapabilityContract } from "./execution-capability-contract.js";
15
15
 
16
- export const CLI_VERSION = "0.5.12";
16
+ export const CLI_VERSION = "0.5.13";
17
17
  export const CLI_API_CONTRACT_VERSION = 10;
18
18
 
19
19
  export function withCliContract(
@@ -53,6 +53,11 @@ Before material action, record:
53
53
  the exact user journey;
54
54
  - required evidence, applicable change boundaries, risk tier, and the few
55
55
  critical invariants that must block integration;
56
+ - for every production-relevant experience ID, outcome and guardrail measures
57
+ with signal, independent source, observation window, threshold, decision and
58
+ owner; if no production outcome is in scope, record that once with a reason;
59
+ - when release is in scope, staged exposure and deterministic promote, hold,
60
+ abort and rollback criteria, rollback mechanism, kill switch and owner;
56
61
  - requested and effective delivery levels, the risk-based safety ceiling, and
57
62
  the chat confirmation receipt;
58
63
  - exclusions and authority boundaries, especially commit, push, Draft PR
@@ -77,19 +82,20 @@ shared source, not twelve new task-local copies. Unaffected views retain their
77
82
  version; unknown As-Is facts remain unknown. Review changes through the team's
78
83
  normal version control and preserve concurrent work.
79
84
 
80
- Use independent agents for design review (including consistency across diagram
81
- kinds), implementation-to-To-Be review, and final PR/evidence review at **every**
82
- level. Reviewers inspect the full affected path and unchanged linked views. Fix
83
- findings and obtain independent re-review. A changed requirement or baseline
85
+ Freeze and follow the risk-proportionate review plan from the selected delivery
86
+ level. Level 3 may consolidate design, implementation-to-To-Be, affected-view and
87
+ final evidence review into one independent final-candidate review. Inspect changed
88
+ views and their dependency links; retain unrelated views by exact revision. Fix
89
+ findings and independently re-review the affected behavior. A changed bound input
84
90
  invalidates affected approval; do not rewrite To-Be to disguise a failed result.
85
91
 
86
- PR creation and post-creation verification include the SQM loop described in the
87
- reference. Announce this requirement when starting Delivery Loop. Running SQM
88
- still follows its explicit opt-in/authority rules; if opt-in or access is absent,
89
- retain the unmet SQM gate and continue independent work. Ordinary PR work outside
90
- Delivery Loop does not activate this workflow. Record known incident coverage and
91
- unknown coverage separately; a green proof with no applicable rules does not prove
92
- that historical failures cannot recur.
92
+ When SQM is explicitly opted in, apply the identity-aware SQM plan described in
93
+ the reference. Announce its applicability when starting Delivery Loop. Reuse a
94
+ verifiable proof only for exactly unchanged bound inputs; rerun for changed identity
95
+ or newly applicable PR-only checks. If required access is absent, retain the unmet
96
+ gate and continue independent work. Ordinary PR work outside Delivery Loop does
97
+ not activate SQM. Record known and unknown incident coverage separately; zero
98
+ applicable rules do not prove that historical failures cannot recur.
93
99
 
94
100
  ## Maintain the acceptance checklist outside the conversation
95
101
 
@@ -290,10 +296,11 @@ Run focused regression tests first, then formatting, lint, type checking, unit
290
296
  tests, and the actual affected runtime. Apply only the relevant lanes from
291
297
  [the change-boundary matrix](references/change-boundary-matrix.md).
292
298
 
293
- After local verification at every level, have an independent agent compare the
294
- implementation and observed user outcomes to the frozen To-Be and original request.
295
- Include cross-view consistency, omissions and applicable historical regressions.
296
- Fix findings narrowly and independently re-review the affected behavior.
299
+ After local verification, execute the frozen review plan for the effective level
300
+ and governing repository. The independent reviewer compares the implementation
301
+ and observed outcomes to the frozen To-Be and original request, including affected
302
+ cross-view links, omissions and applicable historical regressions. Fix findings
303
+ narrowly and independently re-review the affected behavior.
297
304
 
298
305
  Give the independent verifier the acceptance sources and raw evidence, not just
299
306
  the implementer's summary. Use a separate session/agent for every review; another model can help but model count is not quality evidence.
@@ -308,11 +315,14 @@ production configuration, and CI or security-policy changes.
308
315
 
309
316
  ### 4. Coordinate through Draft PR and integration state
310
317
 
311
- Before creating a PR, pass the independent To-Be review and pre-PR SQM loop.
312
318
  After authority for the required remote changes exists, record reproduction,
313
319
  tests, evidence, companion revisions, skipped checks, and remaining gates in the
314
- Draft PR. After creation, run a fresh SQM loop bound to the actual PR head/base
315
- and have an independent agent review the PR diff, proofs and incident coverage.
320
+ Draft PR so the exact remote candidate can be inspected. Before Ready or merge,
321
+ pass the reviews and explicitly opted-in SQM gates required by the frozen plan.
322
+ Rerun a review or SQM gate only when its bound head,
323
+ base, diff, specification, policy, evidence, environment or SQM bundle changed,
324
+ or when a PR-only check became applicable. Otherwise retain the verified receipt
325
+ and record its exact identity instead of duplicating ceremony.
316
326
  Before Ready or merge, combine the latest base with required companion
317
327
  and parallel changes, rerun the affected path, and confirm current CI and
318
328
  mergeability. Without combined-state proof, report `pr_only_verified`, not
@@ -342,6 +352,13 @@ remote merge result. Wait until the target environment reports the exact merge
342
352
  revision or a verifiably derived image digest. Confirm health and runtime
343
353
  identity before live verification.
344
354
 
355
+ For a production-impacting change, release through the frozen stages. At each
356
+ stage observe the named outcome and guardrail signals for the frozen window, then
357
+ apply the deterministic decision: promote only when all promotion thresholds pass;
358
+ hold when evidence is incomplete; abort or rollback when a halt threshold fires.
359
+ Verify the rollback mechanism and kill switch before exposure. Record the owner
360
+ and a receipt for every decision. A release plan is not evidence that a stage ran.
361
+
345
362
  ### 7. Verify the deployed user journey
346
363
 
347
364
  Repeat the relevant Chrome journey against the deployed environment. Include
@@ -354,6 +371,10 @@ history or claim `done`. Save the exact revision, environment, input, Run ID,
354
371
  timestamps, visible state, network evidence, and logs, then add a boundary
355
372
  regression before re-entering implementation.
356
373
 
374
+ After the frozen observation window, evaluate the production outcome and guardrail
375
+ measures for the same experience IDs. A functioning deployment is not successful
376
+ delivery when the intended outcome misses its threshold or a guardrail is breached.
377
+
357
378
  ### Feed repaired delivery failures back into SQM
358
379
 
359
380
  When an observed failure was repaired, prepare one bounded `delivery.json` record
@@ -374,10 +395,11 @@ and validation ordering still require Delivery Loop execution controls.
374
395
 
375
396
  Remove scoped fixtures and prove the residual count is zero. Deliver a compact
376
397
  completion receipt containing shared-spec baseline/To-Be revisions and status,
377
- cross-view and implementation review results, pre/post-PR SQM proofs and historical
378
- coverage gaps, repository revisions, PR and CI, independent
398
+ the frozen review plan and identity-bound review results, applicable SQM proofs
399
+ and their reuse/rerun decision, historical coverage gaps, repository revisions, PR and CI, independent
379
400
  review, container/runtime results, local Chrome evidence, merge and deployment
380
- identity, deployed Chrome evidence, artifacts, cleanup, skipped gates, and
401
+ identity, rollout-stage and outcome/guardrail results, deployed Chrome evidence,
402
+ artifacts, cleanup, skipped gates, and
381
403
  residual risk. Convert recurring findings into the smallest maintainable rule,
382
404
  test, static check, or source-of-truth update selected by the five-response
383
405
  comparison. Bound debt cleanup by scope, change size, and completion criteria;
@@ -24,6 +24,13 @@ together for the beneficiary to succeed, include an end-to-end criterion in
24
24
  addition to narrower diagnostic criteria. Independent coverage review compares
25
25
  the experience contract and original request to the frozen rows before coding.
26
26
 
27
+ For production-relevant experience IDs, extend the same charter with outcome and
28
+ guardrail measures. Each measure names the signal, independent source,
29
+ observation window, threshold, resulting decision and owner. A rollout policy
30
+ references those measure IDs for promotion and halt decisions. These fields are
31
+ part of the same acceptance source and readable results view, not a second
32
+ success checklist.
33
+
27
34
  Each criterion has two layers:
28
35
 
29
36
  - `user`: a goal-driven user journey with visible interactions and evidence of the
@@ -64,6 +71,11 @@ After every evaluated action (test, repair, review or changed inputs):
64
71
  All rows, including pending/failed ones, stay visible. A green matrix cannot advance
65
72
  an unrelated gate or prove merge/deployment. Validate again for the final environment.
66
73
 
74
+ During staged release, append the stage, exposure, observed window, measure
75
+ results and deterministic `promote`, `hold`, `abort` or `rollback` decision to
76
+ the current results and receipt. Planned thresholds are not observations. A
77
+ healthy deployment cannot replace a missed outcome or breached guardrail.
78
+
67
79
  After the exact candidate is built in a production-like local environment, run
68
80
  the complete experience and regenerate `checklist.md` for that same revision,
69
81
  environment, fixture, and input. Read the user and system rows together as a
@@ -37,11 +37,11 @@ messages.
37
37
 
38
38
  | Level | Name | Intended work | Verification and review policy |
39
39
  | --- | --- | --- | --- |
40
- | 1 | Maximum assurance | Authentication, payments, destructive or irreversible changes, production security/configuration, sensitive data, and risky migrations | Exercise all affected and adjacent invariants, failure/recovery and rollback paths; require the broadest applicable independent review and repository gates. |
41
- | 2 | Protected | Persistence, permissions, shared contracts, external side effects, cross-repository changes, and medium-risk migrations | Verify both evidence layers for every affected outcome, shared boundaries, retry/idempotency and recovery; require independent review of the change and affected integrations. |
42
- | 3 | Balanced (default) | Normal features and bug fixes with bounded, reversible impact | Run focused regressions, applicable static checks, the real affected runtime and main user journey; require independent design/To-Be/PR review, scoped to the affected path. |
43
- | 4 | Fast | Small, isolated, reversible UI or implementation changes | Run the direct regression and real affected path; add broader checks only for touched shared boundaries; keep mandatory review and evidence gates. |
44
- | 5 | Fastest | Copy, documentation, comments, metadata, or similarly trivial changes with no runtime/data/security effect | Verify the changed artifact, final diff, packaging/link integrity where applicable, and every repository-mandated check; do not claim untested runtime behavior. |
40
+ | 1 | Maximum assurance | Authentication, payments, destructive or irreversible changes, production security/configuration, sensitive data, and risky migrations | Exercise all affected and adjacent invariants, failure/recovery and rollback paths. Use separate independent design, implementation-boundary, and final PR/evidence reviews plus every repository gate. |
41
+ | 2 | Protected | Persistence, permissions, shared contracts, external side effects, cross-repository changes, and medium-risk migrations | Verify both evidence layers, shared boundaries, retry/idempotency, staged rollout and recovery. Require independent boundary/design and final candidate/integration review; combine them only when the reviewed inputs are exactly identical. |
42
+ | 3 | Balanced (default) | Normal features and bug fixes with bounded, reversible impact | Run focused regressions, applicable static checks, the real affected runtime and main user journey. One independent final-candidate review may cover design, implementation, affected views and evidence; repeat only when its bound identity changes. |
43
+ | 4 | Fast | Small, isolated, reversible UI or implementation changes | Run the direct regression and real affected path, adding shared-boundary checks only when touched. Use a scoped independent diff/actual-path review when repository policy or material ambiguity requires it. |
44
+ | 5 | Fastest | Copy, documentation, comments, metadata, or similarly trivial changes with no runtime/data/security effect | Verify the artifact, final diff, packaging/link integrity and repository-mandated checks. Independent review is needed only when governing policy requires it or material ambiguity remains. |
45
45
 
46
46
  Level 3 is the recommendation when no task-specific signal favors another level.
47
47
  Faster levels reduce work only by declaring genuinely unaffected lanes not
@@ -69,8 +69,9 @@ always choose a lower numbered, safer level.
69
69
  Every level preserves:
70
70
 
71
71
  - frozen success criteria and independent acceptance sources;
72
- - shared system As-Is/To-Be, cross-view consistency and independent agent reviews;
73
- - pre/post-PR SQM gates under explicit opt-in, with historical coverage reported;
72
+ - shared system As-Is/To-Be and consistency of affected cross-view links;
73
+ - the frozen risk-proportionate review plan and every governing repository review;
74
+ - SQM only under explicit opt-in, with identity-bound proof and historical coverage reported;
74
75
  - honest state transitions and current revision/context binding;
75
76
  - required user/system evidence for applicable outcomes;
76
77
  - protection of user work, secrets, data integrity, and tenant boundaries;
@@ -78,3 +79,16 @@ Every level preserves:
78
79
  - repository-required tests, reviews, CI, and approval/authority boundaries.
79
80
 
80
81
  All levels retain the [minimum functional floor](minimum-completion.md): requirement mapping, actual operation/result, and independent postcondition/integration observations. Persistence requires reload and stored-state checks. Non-runtime work verifies its real consumer/artifact; no imaginary DB or API checks are added. The minimum checker has no level-based bypass.
82
+
83
+ ## Reuse instead of duplicate ceremony
84
+
85
+ Freeze the review and SQM plan in the charter. Reuse a passing review or SQM
86
+ proof only when its repository, head, base, working-tree/diff, specification,
87
+ policy, knowledge bundle, evidence and relevant environment identities are exactly
88
+ unchanged and the proof remains verifiable. A PR URL alone is not a changed input.
89
+ Rerun the affected review or SQM phase when any bound identity changes or when a
90
+ PR-only scanner, policy or integration check first becomes applicable.
91
+
92
+ Do not reopen or re-attest every shared view. Inspect the changed views and their
93
+ declared dependency links; retain unchanged views by exact revision. Do not turn a
94
+ failed, unavailable or applicable gate into `not_applicable` to save time.
@@ -139,8 +139,8 @@ external provider calls are introduced.
139
139
 
140
140
  The floor's stable criterion rows trace back to requirement IDs in the team's shared
141
141
  As-Is/To-Be specification. Follow [shared system specifications](shared-system-specifications.md)
142
- for purpose, user experience, cross-view consistency, independent reviews and
143
- pre/post-PR SQM. These are controller workflow obligations, not fields magically
142
+ for purpose, user experience, cross-view consistency, risk-proportionate independent
143
+ reviews and explicitly opted-in, identity-aware SQM. These are controller workflow obligations, not fields magically
144
144
  validated by the minimum checker. The CLI gate verifies minimum structured evidence;
145
145
  it does not launch every design review or SQM phase on its own. Standalone hosts
146
146
  remain responsible for executing this skill and cannot claim an enforced host hook.
@@ -34,9 +34,11 @@ At the start of each run:
34
34
  5. Link each experience ID to workflow, screen/control, state/sequence, API/event,
35
35
  data/relationship, permission and runtime boundaries as applicable. Map these
36
36
  stable system IDs to the run's criterion rows and implementation/evidence locations.
37
- 6. Obtain independent design review before implementation. Supply the original
38
- request, shared baseline, To-Be and linked unchanged views. Missing evidence or
39
- contradictory views remains pending/failed, never silently accepted.
37
+ 6. Freeze the delivery-level review plan. At levels that require a separate
38
+ design review, obtain it before implementation; at level 3 the independent
39
+ final-candidate review may cover design, implementation and evidence together.
40
+ Supply the original request, shared baseline, To-Be and affected links. Missing
41
+ evidence or contradictory views remains pending/failed, never silently accepted.
40
42
 
41
43
  After the exact local candidate is built, update the existing acceptance matrix
42
44
  and its readable table for those same experience IDs. Compare expected experience,
@@ -68,13 +70,15 @@ interface views; do not invent application screens, APIs, tables or infrastructu
68
70
 
69
71
  ## Review evidence and neutral agents
70
72
 
71
- All reviews use independent agents/sessions, including design, cross-view
72
- consistency, implementation-versus-To-Be, verification changes and PR review.
73
- An agent must not approve work it authored. A reviewer can re-review fixes to its
74
- findings if it did not implement the fixes. Supply raw sources, not desired answers;
75
- require inspected references, discrepancies, uncertainty and a reasoned verdict.
76
- Use the repository-required model (gpt-5.5 / xhigh where mandated and available).
77
- If independent review is unavailable, report that gate as blocked.
73
+ Required reviews use independent agents/sessions; an agent must not approve work
74
+ it authored. Follow the risk-proportionate plan in `delivery-levels.md` instead of
75
+ creating design, cross-view, implementation and PR reviews by default. A reviewer
76
+ may cover several review kinds in one receipt when the candidate inputs are the
77
+ same, and can re-review fixes it did not implement. Supply raw sources, not desired
78
+ answers; require inspected references, discrepancies, uncertainty and a reasoned
79
+ verdict. Use the repository-required model (gpt-5.5 / xhigh where mandated and
80
+ available). If a required independent review is unavailable, report that gate as
81
+ blocked.
78
82
 
79
83
  Persist a compact review receipt: reviewer/session identity, review kind, original
80
84
  request and shared baseline/To-Be references, candidate revisions, inspected views
@@ -122,17 +126,22 @@ Use concrete regressions for relevant uncovered incidents and have an independen
122
126
  reviewer assess coverage; keep missing incident history or untested boundaries
123
127
  visible. No finite test promises absence of all future bugs.
124
128
 
125
- Pre-PR: implementation-to-To-Be review and relevant regressions pass, then run SQM;
126
- repair findings within the bounded loop, re-review changed work, and rerun against
127
- the final candidate before creation. On unavailable/failed proof, withhold creation
128
- and report the exact blocker while continuing independent authorized preparation.
129
-
130
- Post-PR: fetch the actual PR head/base and check out/verify that combination, run
131
- both SQM phases again into new paths, inspect proof and current CI, and obtain
132
- independent PR/evidence review. The pre-PR proof cannot stand in for this run.
133
- If it fails, retain the PR as draft/not-ready with unmet gates recorded, repair and
134
- rerun affected independent reviews and SQM. A changed head/base/spec or relevant
135
- bundle invalidates affected approvals and proofs; preserve old receipts as history.
129
+ Draft creation: after authority and local regressions, publish a Draft so the exact
130
+ remote candidate can be inspected. Draft existence is not gate approval.
131
+
132
+ Before Ready or merge, pass the frozen review plan. When the explicitly opted-in
133
+ plan requires SQM, run it against the final candidate; repair findings within the
134
+ bounded loop and rerun affected gates. On an unavailable/failed required proof,
135
+ retain Draft/not-ready and report the exact blocker while continuing independent
136
+ authorized preparation.
137
+
138
+ Post-PR: fetch and verify the actual PR head/base and current CI. Reuse the pre-PR
139
+ SQM proof only when repository, head, base, working-tree/diff, specification,
140
+ policy, knowledge bundle, evidence and relevant environment identities are exactly
141
+ unchanged and the signed proof verifies. Rerun into a new path when any identity
142
+ changed or a PR-only scanner/policy becomes applicable. Apply the same rule to
143
+ independent review. If a required gate fails, retain the PR as draft/not-ready,
144
+ repair and rerun affected gates. Preserve superseded receipts as history.
136
145
  Promote Ready only with current required gates, including applicable combined-state
137
146
  verification. Merge and deployment still require their own authority and evidence.
138
147
 
@@ -22,7 +22,8 @@ Persist one controller-owned snapshot with at least:
22
22
  - frozen acceptance charter digest, current matrix/table paths and digests,
23
23
  superseded matrix snapshots, and the acceptance validator result for each gate;
24
24
  - verification-boundary changes, deferred-debt references, and applicable
25
- outcome and guardrail measures;
25
+ outcome and guardrail measures, rollout policy, current rollout stage and
26
+ latest deterministic release decision;
26
27
  - attempt number, retry/time/cost budgets, and failure fingerprints;
27
28
  - append-only receipt IDs, evidence references, timestamps, and cleanup state.
28
29
 
@@ -46,6 +47,8 @@ Use the smallest applicable receipts rather than free-form completion claims:
46
47
  - `ChromeUserJourneyReceipt`
47
48
  - `MergeReceipt`
48
49
  - `DeploymentIdentityReceipt`
50
+ - `RolloutStageReceipt`
51
+ - `OutcomeEvaluationReceipt`
49
52
  - `HealthReceipt`
50
53
  - `LiveEvidenceReceipt`
51
54
  - `CleanupReceipt`
@@ -108,6 +111,8 @@ Evidence is revision-bound:
108
111
  | Merge SHA changes | Deployment identity and live evidence |
109
112
  | Deployed revision changes | Health and live evidence |
110
113
  | Browser profile, tenant, or fixture changes | Affected user-journey evidence |
114
+ | Outcome/guardrail signal, source, window, threshold, decision, or owner changes | Affected release and outcome evidence |
115
+ | Rollout stage, exposure, recovery control, or kill switch changes | Affected rollout and later live evidence |
111
116
 
112
117
  Do not discard historical receipts; mark them superseded for the current gate.
113
118
 
@@ -146,11 +151,13 @@ done =
146
151
  && deployed_exact_revision
147
152
  && health_verified
148
153
  && live_verified
154
+ && production_outcome_evaluated
149
155
  && cleanup_verified
150
156
  && completion_receipt_created
151
157
  ```
152
158
 
153
- Remove only genuinely non-applicable terms and record why. If merge or deploy
159
+ Remove only genuinely non-applicable terms and record why; production outcome
160
+ evaluation is non-applicable when no production-impacting release is in scope. If merge or deploy
154
161
  is outside current authority, stop at the highest proven pre-authority state.
155
162
 
156
163
  Acceptance evidence must cover every applicable criterion for the target gate,
@@ -4,6 +4,7 @@ import { lstat, readFile, realpath } from "node:fs/promises";
4
4
  import path from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import { isDeepStrictEqual } from "node:util";
7
+ import { normalizeProductionContract } from "./production-contract.mjs";
7
8
  import {
8
9
  checkAcceptance,
9
10
  contextDigest,
@@ -49,6 +50,7 @@ export function minimumCharter(charter) {
49
50
  run_id: charter.id,
50
51
  source,
51
52
  experience,
53
+ ...normalizeProductionContract(charter.completion_contract, ids),
52
54
  criteria: charter.acceptance_criteria.map((outcome, index) => ({
53
55
  id: ids[index],
54
56
  outcome,
@@ -0,0 +1,158 @@
1
+ // Shared by the CLI and the independently installed skill. Keep this module
2
+ // self-contained so validation never depends on the repository checkout.
3
+ const cleanString = value => typeof value === "string" ? value.trim() : "";
4
+ const record = value => value && typeof value === "object" && !Array.isArray(value) ? value : {};
5
+ const uniqueStrings = value => [...new Set(Array.isArray(value) ? value.map(cleanString).filter(Boolean) : [])];
6
+ const criterionIdPattern = /^[A-Za-z][A-Za-z0-9]*(?:[-_.][A-Za-z0-9]+)*$/;
7
+
8
+ export function normalizeProductionContract(source, ids) {
9
+ const outcome_contract = normalizeOutcomeContract(source.outcome_contract, ids);
10
+ return { outcome_contract, rollout_policy: normalizeRolloutPolicy(source.rollout_policy, outcome_contract) };
11
+ }
12
+
13
+ function normalizeOutcomeContract(value, criterionIds) {
14
+ const source = record(value);
15
+ const criteria = new Set(criterionIds);
16
+ const outcomeMeasures = normalizeMeasures(
17
+ source.outcome_measures,
18
+ criteria,
19
+ new Set(["promote", "hold"]),
20
+ "outcome",
21
+ );
22
+ const guardrailMeasures = normalizeMeasures(
23
+ source.guardrail_measures,
24
+ criteria,
25
+ new Set(["hold", "abort", "rollback"]),
26
+ "guardrail",
27
+ );
28
+ const allIds = [
29
+ ...outcomeMeasures.map((measure) => measure.id),
30
+ ...guardrailMeasures.map((measure) => measure.id),
31
+ ];
32
+ if (new Set(allIds).size !== allIds.length) {
33
+ throw new Error("Duplicate outcome or guardrail measure IDs");
34
+ }
35
+ return {
36
+ outcome_measures: outcomeMeasures,
37
+ guardrail_measures: guardrailMeasures,
38
+ };
39
+ }
40
+
41
+ function normalizeMeasures(value, criterionIds, allowedDecisions, kind) {
42
+ if (value === undefined || value === null) return [];
43
+ if (!Array.isArray(value)) throw new Error(`Invalid ${kind} measures`);
44
+ const measures = value.map((item) => {
45
+ const source = record(item);
46
+ const measure = {
47
+ id: cleanString(source.id),
48
+ criterion_id: cleanString(source.criterion_id),
49
+ signal: cleanString(source.signal),
50
+ source: cleanString(source.source),
51
+ window: cleanString(source.window),
52
+ threshold: cleanString(source.threshold),
53
+ decision: cleanString(source.decision).toLowerCase(),
54
+ owner: cleanString(source.owner),
55
+ };
56
+ if (
57
+ !criterionIdPattern.test(measure.id) ||
58
+ !criterionIds.has(measure.criterion_id) ||
59
+ !measure.signal ||
60
+ !measure.source ||
61
+ !measure.window ||
62
+ !measure.threshold ||
63
+ !allowedDecisions.has(measure.decision) ||
64
+ !measure.owner
65
+ ) {
66
+ throw new Error(`Invalid ${kind} measure`);
67
+ }
68
+ return measure;
69
+ });
70
+ if (new Set(measures.map((measure) => measure.id)).size !== measures.length) {
71
+ throw new Error(`Duplicate ${kind} measure IDs`);
72
+ }
73
+ return measures;
74
+ }
75
+
76
+ function normalizeRolloutPolicy(value, outcomeContract) {
77
+ const source = record(value);
78
+ const required = source.required === true;
79
+ if (!required) {
80
+ return {
81
+ required: false,
82
+ reason: cleanString(source.reason) || "No production rollout is in scope",
83
+ stages: [],
84
+ promotion_requires: [],
85
+ halt_on: [],
86
+ rollback: null,
87
+ kill_switch: null,
88
+ };
89
+ }
90
+ const stages = normalizeRolloutStages(source.stages);
91
+ const outcomeIds = new Set(
92
+ outcomeContract.outcome_measures.map((measure) => measure.id),
93
+ );
94
+ const guardrailIds = new Set(
95
+ outcomeContract.guardrail_measures.map((measure) => measure.id),
96
+ );
97
+ const promotionRequires = uniqueStrings(source.promotion_requires);
98
+ const haltOn = uniqueStrings(source.halt_on);
99
+ const rollback = normalizeRecoveryControl(source.rollback, "rollback");
100
+ const killSwitch = normalizeRecoveryControl(source.kill_switch, "kill switch");
101
+ if (
102
+ stages.length < 2 ||
103
+ outcomeIds.size === 0 ||
104
+ promotionRequires.length !== outcomeIds.size ||
105
+ promotionRequires.some((id) => !outcomeIds.has(id)) ||
106
+ guardrailIds.size === 0 ||
107
+ haltOn.length !== guardrailIds.size ||
108
+ haltOn.some((id) => !guardrailIds.has(id))
109
+ ) {
110
+ throw new Error("Invalid progressive rollout policy");
111
+ }
112
+ return {
113
+ required: true,
114
+ reason: cleanString(source.reason) || "Production impact requires progressive exposure",
115
+ stages,
116
+ promotion_requires: promotionRequires,
117
+ halt_on: haltOn,
118
+ rollback,
119
+ kill_switch: killSwitch,
120
+ };
121
+ }
122
+
123
+ function normalizeRolloutStages(value) {
124
+ if (!Array.isArray(value)) throw new Error("Invalid rollout stages");
125
+ const stages = value.map((item) => {
126
+ const source = record(item);
127
+ const stage = {
128
+ id: cleanString(source.id),
129
+ exposure: cleanString(source.exposure),
130
+ observation_window: cleanString(source.observation_window),
131
+ };
132
+ if (
133
+ !criterionIdPattern.test(stage.id) ||
134
+ !stage.exposure ||
135
+ !stage.observation_window
136
+ ) {
137
+ throw new Error("Invalid rollout stage");
138
+ }
139
+ return stage;
140
+ });
141
+ if (new Set(stages.map((stage) => stage.id)).size !== stages.length) {
142
+ throw new Error("Duplicate rollout stage IDs");
143
+ }
144
+ return stages;
145
+ }
146
+
147
+ function normalizeRecoveryControl(value, label) {
148
+ const source = record(value);
149
+ const control = {
150
+ mechanism: cleanString(source.mechanism),
151
+ owner: cleanString(source.owner),
152
+ };
153
+ if (!control.mechanism || !control.owner) {
154
+ throw new Error(`Invalid ${label} control`);
155
+ }
156
+ return control;
157
+ }
158
+
@@ -1,3 +1,4 @@
1
+ import { normalizeProductionContract } from "../codex-plugins/simy/skills/agentic-delivery-loop/scripts/production-contract.mjs";
1
2
  import { cleanString, stringArray } from "./shared.js";
2
3
 
3
4
  export const COMPLETION_CONTRACT_VERSION = 2;
@@ -15,13 +16,16 @@ export function buildCompletionContract({
15
16
  const proofRequired = stringArray(source.proof_required);
16
17
  const criteria = stringArray(acceptanceCriteria);
17
18
  const userOutcome = cleanString(source.user_outcome) || cleanString(requirement);
19
+ const ids = criterionIds(source.criterion_ids, criteria.length);
20
+ const productionContract = normalizeProductionContract(source, ids);
18
21
  return {
19
22
  version: COMPLETION_CONTRACT_VERSION,
20
23
  user_outcome: userOutcome,
21
24
  minimum_scope:
22
25
  minimumScope.length > 0 ? minimumScope : criteria,
23
- criterion_ids: criterionIds(source.criterion_ids, criteria.length),
26
+ criterion_ids: ids,
24
27
  experience: normalizeExperience(source.experience, userOutcome),
28
+ ...productionContract,
25
29
  non_goals: stringArray(nonGoals),
26
30
  proof_required: uniqueStrings([
27
31
  "criterion_evidence",
@@ -65,6 +65,9 @@ export function buildIndependentAuditInstruction(charter, attempt) {
65
65
  "Treat executor claims as untrusted until supported by repository evidence.",
66
66
  "Compare the original request and frozen experience to the complete stable criterion-ID list and implementation mapping. Report forgotten features, dead controls, unsaved results and broken integration. Open the raw minimum evidence, verify its oracle and actual runtime observations; matching expected/actual JSON and hashes alone do not prove truth. Check reload/session return and persisted values/ownership when saving is required.",
67
67
  "For every stable criterion ID, return a criterion_results entry only after directly checking its cited proof and observed outcome.",
68
+ "For production-relevant criteria, verify that each outcome and guardrail measure names its signal, independent source, observation window, threshold, decision, owner, and criterion ID.",
69
+ "When rollout_policy.required=true, verify that staged exposure and deterministic promote, hold, abort, rollback, and kill-switch controls are frozen before release. Do not treat a rollout plan as deployed evidence.",
70
+ "Apply the frozen delivery-level review plan: reuse a prior review only when its exact head, base, specification, policy, evidence, and environment identities are unchanged; otherwise review the affected boundary again.",
68
71
  charter.evidence_policy?.browser_required
69
72
  ? "Open and inspect the actual local screenshots or videos. Return visual_review.inspected=true only after checking their visible content against the acceptance criteria."
70
73
  : "Do not require visual evidence when the Charter does not require browser evidence.",
@@ -43,6 +43,9 @@ export function buildCodingInstruction(
43
43
  `Usable outcome: ${charter.completion_contract?.experience?.usable_outcome || "not recorded"}`,
44
44
  `Recovery experience: ${charter.completion_contract?.experience?.recovery || "not recorded"}`,
45
45
  `Forbidden experiences:\n${bullets(charter.completion_contract?.experience?.forbidden_experiences || [])}`,
46
+ `Outcome measures: ${JSON.stringify(charter.completion_contract?.outcome_contract?.outcome_measures || [])}`,
47
+ `Guardrail measures: ${JSON.stringify(charter.completion_contract?.outcome_contract?.guardrail_measures || [])}`,
48
+ `Progressive rollout policy: ${JSON.stringify(charter.completion_contract?.rollout_policy || { required: false })}`,
46
49
  `Minimum scope:\n${bullets(charter.completion_contract?.minimum_scope || charter.acceptance_criteria)}`,
47
50
  `Non-goals:\n${bullets(charter.completion_contract?.non_goals || charter.thread_state.non_goals)}`,
48
51
  `Proof required:\n${bullets(charter.completion_contract?.proof_required || [])}`,