scene-capability-engine 3.0.6 → 3.0.8

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/CHANGELOG.md CHANGED
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
 
10
10
  ### Added
11
11
  - **Capability lexicon hard gate end-to-end defaultization**: `sce auto handoff run` and `sce auto handoff capability-matrix` now enforce unknown Moqui capability alias blocking by default (expected/provided), emit lexicon gate telemetry into release evidence, and promote those signals into governance risk/concern/recommendation/close-loop block decisions.
12
+ - **Moqui release summary report helper**: Added `node scripts/moqui-release-summary.js` (and npm alias `npm run report:moqui-summary`) to consolidate handoff evidence + baseline + lexicon + capability-matrix into a single release-gate verdict (`passed|failed|incomplete`) with remediation commands.
12
13
  - **SCE naming consolidation + compatibility bridge**: Rebranded product naming to `Scene Capability Engine`, moved package to `scene-capability-engine`, promoted `sce` as the primary CLI command, and preserved `sco` / `sce` / `scene-capability-engine` aliases for migration continuity.
13
14
  - **Official template library v1.5.0 alignment**: Synced with `scene-capability-engine-templates` `v1.5.0`, adding scene orchestration template coverage for canvas visualization, interaction hardening, execution playbook, dependency drilldown, decision cockpit, runbook export, action queue orchestration, action pack export, and unified scene governance closure.
14
15
  - **Branding consistency release guard**: Added `test:brand-consistency` to block publish when legacy repository/package/product naming reappears in tracked source files.
@@ -138,6 +139,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
138
139
  - **Autonomous operator UX**: Expanded docs with semantic decomposition and live stream behavior for close-loop command usage.
139
140
 
140
141
  ### Fixed
142
+ - **Orchestrator 429 backoff stall behavior**: `orchestration-engine` now clamps `Retry-After` retry waits by `rateLimitBackoffMaxMs` and interrupts pending retry sleeps immediately on `stop()`, preventing long backoff waits from appearing as deadlocks.
141
143
  - **Scene runtime 429 failure behavior under high request pressure**: Moqui HTTP client now retries on `429` with `Retry-After` support and bounded exponential backoff, reducing multi-agent stalls caused by transient service-side request limits.
142
144
  - **Moqui adapter matching fallback safety**: `spec.erp.*` bindings no longer get captured by Moqui handler when no adapter config exists, preventing false hard-fail paths and restoring expected simulator fallback behavior.
143
145
  - **Controller summary semantics**: `close-loop-controller` now reports final `pending_goals` from the persisted queue snapshot and only marks `cycle-limit-reached` as exhausted when pending work remains (or empty-polling mode explicitly consumes cycle budget).
package/README.md CHANGED
@@ -253,6 +253,7 @@ sequenceDiagram
253
253
  - 🌍 **[Environment Management](docs/environment-management-guide.md)** - Multi-environment configuration
254
254
  - 📦 **[Multi-Repository Management](docs/multi-repo-management-guide.md)** - Manage multiple Git repositories
255
255
  - 🎭 **[Scene Runtime](docs/scene-runtime-guide.md)** - Template engine, quality pipeline, ontology, Moqui ERP
256
+ - ♻️ **[Moqui Standard Rebuild](docs/moqui-standard-rebuild-guide.md)** - Rebuild bootstrap bundle + page-copilot contract from metadata
256
257
  - 🤖 **[Multi-Agent Coordination](docs/multi-agent-coordination-guide.md)** - Parallel agent coordination
257
258
  - 📈 **[Value Observability](docs/value-observability-guide.md)** - KPI snapshot, baseline, trend, gate-ready evidence
258
259
  - 🔌 **[Integration Modes](docs/integration-modes.md)** - Three ways to integrate sce
@@ -23,6 +23,12 @@ Use this checklist before each integration batch.
23
23
  - `ontology_validation` exists and is recent.
24
24
  - Dependency relations (`depends_on`) are present for multi-spec batches.
25
25
  - High-risk gaps have mitigation notes.
26
+ - Moqui baseline matrix has no hard-gate regressions:
27
+ - `compare.coverage_matrix_regressions.length == 0`
28
+ - `summary.coverage_matrix.baseline_passed.rate_percent == 100`
29
+ - Capability lexicon has no unknown aliases:
30
+ - `summary.expected_unknown_count == 0`
31
+ - `summary.provided_unknown_count == 0`
26
32
 
27
33
  ## sce Acceptance Commands
28
34
 
@@ -34,6 +40,10 @@ npx sce auto handoff run --manifest ../331-poc/docs/handoffs/handoff-manifest.js
34
40
 
35
41
  npx sce auto handoff template-diff --manifest ../331-poc/docs/handoffs/handoff-manifest.json --json
36
42
  npx sce auto handoff regression --session-id latest --json
43
+ npx sce auto handoff capability-matrix --manifest ../331-poc/docs/handoffs/handoff-manifest.json --fail-on-gap --json
44
+ npx sce scene moqui-baseline --compare-with .kiro/reports/release-evidence/moqui-template-baseline-prev.json --fail-on-portfolio-fail --json
45
+ node scripts/moqui-lexicon-audit.js --manifest ../331-poc/docs/handoffs/handoff-manifest.json --fail-on-gap --json
46
+ node scripts/moqui-release-summary.js --fail-on-gate-fail --json
37
47
  ```
38
48
 
39
49
  ## Scene Package Gate Commands
@@ -602,7 +602,7 @@ Close-loop batch (`sce auto close-loop-batch <goals-file>`) options:
602
602
  - Batch summary includes `resource_plan` (budget/effective parallel/per-goal maxParallel/scheduling strategy/aging/starvation wait metrics/criticality summary) and `metrics` (`success_rate_percent`, `status_breakdown`, `average_sub_specs_per_goal`, `average_replan_cycles_per_goal`, `total_rate_limit_signals`, `average_rate_limit_signals_per_goal`, `total_rate_limit_backoff_ms`)
603
603
  - Under budget mode, scheduler is complexity-weighted (`goal_weight`/`scheduling_weight`) so higher-complexity goals consume more shared slots and can reduce same-batch concurrency.
604
604
  - Batch summary includes `batch_retry` telemetry (strategy, until-complete mode, configured/max/performed rounds, exhausted flag, per-round history).
605
- - Under `--batch-retry-strategy adaptive`, retry history includes rate-limit pressure and next-round backpressure decisions (`applied_batch_parallel`, `next_batch_parallel`, `adaptive_backpressure_applied`).
605
+ - Under `--batch-retry-strategy adaptive`, retry history includes rate-limit pressure and next-round backpressure decisions (`applied_batch_parallel`, `next_batch_parallel`, `adaptive_backpressure_applied`, `backpressure_level`). Severe pressure automatically halves `batch_parallel` / `batch_agent_budget` for the next retry round.
606
606
  - Batch summary includes `batch_session` metadata when persisted (session id + file path).
607
607
  - When using `--decompose-goal`, summary includes `generated_from_goal` metadata (strategy, target count, produced count, clause/category diagnostics, decomposition `quality`, and refinement telemetry).
608
608
 
@@ -833,6 +833,35 @@ Moqui template library lexicon audit (script-level governance helper):
833
833
  - By default, template capability auditing is scoped to `manifest.templates` (when matched), reducing noise from unrelated templates.
834
834
  - Template scope matching normalizes `sce.scene--*` / `kse.scene--*` prefixes, so renamed template namespaces still map correctly.
835
835
 
836
+ Moqui release summary helper (script-level consolidated gate view):
837
+ - `node scripts/moqui-release-summary.js [--evidence <path>] [--baseline <path>] [--lexicon <path>] [--capability-matrix <path>] [--out <path>] [--markdown-out <path>] [--fail-on-gate-fail] [--json]`: merge handoff release-evidence + baseline + lexicon + capability-matrix into one Moqui release gate summary (`passed | failed | incomplete`) with executable remediation recommendations.
838
+ - Default inputs:
839
+ - `.kiro/reports/release-evidence/handoff-runs.json`
840
+ - `.kiro/reports/release-evidence/moqui-template-baseline.json`
841
+ - `.kiro/reports/release-evidence/moqui-lexicon-audit.json`
842
+ - `.kiro/reports/handoff-capability-matrix.json`
843
+ - Default outputs:
844
+ - `.kiro/reports/release-evidence/moqui-release-summary.json`
845
+ - `.kiro/reports/release-evidence/moqui-release-summary.md`
846
+ - `--fail-on-gate-fail` exits with code `2` when summary gate is `failed`.
847
+
848
+ Moqui standard rebuild helper (script-level recovery bootstrap):
849
+ - `node scripts/moqui-standard-rebuild.js [--metadata <path>] [--out <path>] [--markdown-out <path>] [--bundle-out <path>] [--json]`: build a standard Moqui recovery bundle from metadata, including recommended SCE template matrix, recovery spec plan, handoff manifest seed, ontology seed, and page-copilot context contract.
850
+ - Output now includes `recovery.readiness_matrix`, `recovery.readiness_summary`, and `recovery.prioritized_gaps` for template capability matrix scoring and remediation planning.
851
+ - Bundle now includes `rebuild/matrix-remediation.lines` (gap remediation queue lines).
852
+ - Bundle now includes `rebuild/matrix-remediation-plan.json|.md` (gap-to-source-file remediation plan).
853
+ - This workflow is scoped to SCE outputs and does not mutate business project code directly.
854
+ - Recommended usage for rebuild target path: `E:/workspace/331-poc-rebuild` (keep `331-poc` repair stream isolated).
855
+
856
+ Moqui rebuild gate helper (CI/pre-release readiness gate):
857
+ - `node scripts/moqui-rebuild-gate.js [--metadata <path>] [--out <path>] [--markdown-out <path>] [--bundle-out <path>] [--min-ready <n>] [--max-partial <n>] [--max-gap <n>]`: run rebuild and fail when readiness gate is not met (default: ready>=6, partial<=0, gap<=0).
858
+ - npm alias: `npm run gate:moqui-rebuild`
859
+
860
+ Moqui metadata extractor helper (script-level catalog bootstrap):
861
+ - `node scripts/moqui-metadata-extract.js [--project-dir <path>] [--out <path>] [--markdown-out <path>] [--json]`: build a normalized metadata catalog from multiple sources for rebuild automation. Default sources include Moqui XML resources (`entity/service/screen/form/rule/decision`), scene package contracts (`.kiro/specs/**/docs/scene-package.json`), handoff manifest/capability matrix, and handoff evidence JSON.
862
+ - Recommended first step before `moqui-standard-rebuild`.
863
+ - Keep extraction source read-only and run rebuild generation against SCE output directories.
864
+
836
865
  Recommended `.kiro/config/orchestrator.json`:
837
866
 
838
867
  ```json
@@ -855,7 +884,7 @@ Recommended `.kiro/config/orchestrator.json`:
855
884
  }
856
885
  ```
857
886
 
858
- `rateLimit*` settings provide dedicated retry/backoff and adaptive parallel throttling when providers return 429 / too-many-requests errors. Engine retry now also honors `Retry-After` / `try again in ...` hints from provider error messages when present, and pauses launching new pending specs during the active backoff window to reduce request bursts (launch hold remains active even if adaptive parallel throttling is disabled).
887
+ `rateLimit*` settings provide dedicated retry/backoff and adaptive parallel throttling when providers return 429 / too-many-requests errors. Engine retry honors `Retry-After` / `try again in ...` hints from provider error messages and clamps final retry waits by `rateLimitBackoffMaxMs` to avoid unbounded pause windows. During active backoff windows, new pending spec launches are paused to reduce request bursts (launch hold remains active even if adaptive parallel throttling is disabled). `orchestrate stop` now interrupts pending retry waits immediately so long backoff does not look like a deadlock.
859
888
 
860
889
  ### Scene Template Engine
861
890
 
@@ -940,6 +969,9 @@ sce scene moqui-baseline --json
940
969
  # Script alias (same behavior)
941
970
  npm run report:moqui-baseline
942
971
 
972
+ # Consolidated Moqui release gate summary (recommended before tag/publish)
973
+ npm run report:moqui-summary
974
+
943
975
  # Score all scene templates instead of the default Moqui + orchestration subset
944
976
  sce scene moqui-baseline --include-all --json
945
977
 
@@ -960,6 +992,7 @@ sce scene moqui-baseline \
960
992
 
961
993
  Release workflow default:
962
994
  - Publishes `moqui-template-baseline.json` + `moqui-template-baseline.md` as release assets.
995
+ - Publishes `moqui-release-summary.json` + `moqui-release-summary.md` as release review assets.
963
996
  - Enforces baseline portfolio gate by default (`KSE_MOQUI_BASELINE_ENFORCE` defaults to `true` when unset).
964
997
 
965
998
  ### Moqui ERP Integration
@@ -0,0 +1,96 @@
1
+ # Moqui Standard Rebuild Guide
2
+
3
+ This guide bootstraps a standard Moqui recovery bundle using SCE templates and metadata.
4
+ It is designed to avoid direct interference with an in-progress `331-poc` repair stream.
5
+
6
+ ## Goal
7
+
8
+ - Rebuild baseline business capability assets from metadata.
9
+ - Keep original Moqui technology stack unchanged.
10
+ - Add a page-level human/AI copilot dialog contract for contextual fixes.
11
+
12
+ ## Recommended Workspace Strategy
13
+
14
+ 1. Keep SCE in `E:/workspace/kiro-spec-engine` for reusable rebuild tooling.
15
+ 2. Keep business rebuild target isolated (recommended): `E:/workspace/331-poc-rebuild`.
16
+ 3. Do not write generated recovery files into live `331-poc` unless explicitly approved.
17
+
18
+ ## Input Metadata (minimum)
19
+
20
+ Provide a JSON file with at least one of these arrays:
21
+
22
+ - `entities`
23
+ - `services`
24
+ - `screens`
25
+ - `forms`
26
+
27
+ Optional:
28
+
29
+ - `business_rules`
30
+ - `decisions`
31
+
32
+ ## Step 1: Extract Metadata (from Moqui project)
33
+
34
+ ```bash
35
+ node scripts/moqui-metadata-extract.js \
36
+ --project-dir E:/workspace/your-moqui-project \
37
+ --out docs/moqui/metadata-catalog.json \
38
+ --markdown-out docs/moqui/metadata-catalog.md \
39
+ --json
40
+ ```
41
+
42
+ `moqui-metadata-extract` now performs multi-source catalog extraction by default:
43
+ - Moqui XML (`entity/service/screen/form/rule/decision`)
44
+ - `scene-package.json` contracts in `.kiro/specs/**/docs/`
45
+ - `docs/handoffs/handoff-manifest.json`
46
+ - `docs/handoffs/capability-matrix.md`
47
+ - `docs/handoffs/evidence/**/*.json`
48
+ - `.kiro/recovery/salvage/**/*.json` (if present)
49
+
50
+ ## Step 2: Build Rebuild Bundle
51
+
52
+ ```bash
53
+ node scripts/moqui-standard-rebuild.js \
54
+ --metadata docs/moqui/metadata-catalog.json \
55
+ --out .kiro/reports/recovery/moqui-standard-rebuild.json \
56
+ --markdown-out .kiro/reports/recovery/moqui-standard-rebuild.md \
57
+ --bundle-out .kiro/reports/recovery/moqui-standard-bundle \
58
+ --json
59
+ ```
60
+
61
+ ## Step 3: Run Rebuild Readiness Gate (recommended for CI/release)
62
+
63
+ ```bash
64
+ node scripts/moqui-rebuild-gate.js \
65
+ --metadata docs/moqui/metadata-catalog.json \
66
+ --out .kiro/reports/recovery/moqui-standard-rebuild.json \
67
+ --markdown-out .kiro/reports/recovery/moqui-standard-rebuild.md \
68
+ --bundle-out .kiro/reports/recovery/moqui-standard-bundle
69
+ ```
70
+
71
+ Defaults: `ready>=6`, `partial<=0`, `gap<=0`.
72
+
73
+ ## Generated Bundle
74
+
75
+ - `handoff/handoff-manifest.json`: seed manifest for SCE handoff gates.
76
+ - `ontology/moqui-ontology-seed.json`: initial ontology graph seed.
77
+ - `rebuild/recovery-spec-plan.json`: ordered recovery spec list.
78
+ - `rebuild/matrix-remediation.lines`: prioritized remediation queue lines derived from template readiness gaps.
79
+ - `rebuild/matrix-remediation-plan.json`: gap-to-source-file remediation plan for quick 331 execution.
80
+ - `rebuild/matrix-remediation-plan.md`: markdown view of remediation plan.
81
+ - `copilot/page-context-contract.json`: page-context contract for copilot dialog.
82
+ - `copilot/conversation-playbook.md`: operational playbook for human/AI page fixes.
83
+ - rebuild report includes template readiness scoring (`recovery.readiness_matrix`) and prioritized remediation items (`recovery.prioritized_gaps`).
84
+
85
+ ## Default Moqui Template Matrix
86
+
87
+ - `kse.scene--moqui-entity-model-core--0.1.0`
88
+ - `kse.scene--moqui-service-contract-core--0.1.0`
89
+ - `kse.scene--moqui-screen-flow-core--0.1.0`
90
+ - `kse.scene--moqui-form-interaction-core--0.1.0`
91
+ - `kse.scene--moqui-rule-decision-core--0.1.0`
92
+ - `kse.scene--moqui-page-copilot-dialog--0.1.0`
93
+
94
+ ## Next Step for Business Project
95
+
96
+ Use generated assets as input to a dedicated rebuild project (for example `331-poc-rebuild`) and execute normal SCE handoff + gate flows there.
@@ -33,6 +33,42 @@ Emergency bypass exists but is not recommended:
33
33
  - `--no-require-moqui-baseline`
34
34
  - `--no-require-capability-coverage`
35
35
 
36
+ ## Template Capability Matrix Contract
37
+
38
+ Use the baseline report as the canonical matrix contract (`.kiro/reports/moqui-template-baseline.json`):
39
+
40
+ | Matrix Dimension | Meaning | Default Gate Target |
41
+ | --- | --- | --- |
42
+ | `graph_valid.rate_percent` | Ontology graph structural validity | `100%` |
43
+ | `score_passed.rate_percent` | Semantic score above baseline threshold | `100%` |
44
+ | `entity_coverage.rate_percent` | Entity model coverage | `100%` |
45
+ | `relation_coverage.rate_percent` | Entity relation coverage | `100%` |
46
+ | `business_rule_coverage.rate_percent` | Business-rule presence coverage | `100%` |
47
+ | `business_rule_closed.among_covered_rate_percent` | No unmapped rules among covered templates | `100%` |
48
+ | `decision_coverage.rate_percent` | Decision logic presence coverage | `100%` |
49
+ | `decision_closed.among_covered_rate_percent` | No undecided decisions among covered templates | `100%` |
50
+ | `baseline_passed.rate_percent` | Full matrix closure rate | `100%` |
51
+
52
+ Trend regression should stay hard-gated by default:
53
+
54
+ - `compare.coverage_matrix_regressions.length` must be `0`
55
+ - `sce auto handoff run` default `--max-moqui-matrix-regressions 0`
56
+
57
+ ## External POC Handoff Requirements
58
+
59
+ When an upstream business project (e.g. a POC) feeds templates into sce, require the following by default:
60
+
61
+ - Every handoff spec contains:
62
+ - `custom/scene.yaml` with entity/service/screen binding refs
63
+ - `custom/scene-package.json` with explicit `capabilities.expected/provided`
64
+ - ontology semantics for entity, relation, business-rule, and decision dimensions
65
+ - Handoff evidence includes:
66
+ - latest `moqui-template-baseline.json|.md`
67
+ - latest `handoff-capability-matrix` report
68
+ - latest `moqui-lexicon-audit` report
69
+ - ontology batch report from `scene package-publish-batch --dry-run`
70
+ - New/updated templates must provide deterministic IDs and version bumps so baseline comparisons are stable across releases.
71
+
36
72
  ## One-Shot Intake Flow
37
73
 
38
74
  ```bash
@@ -60,6 +96,11 @@ node scripts/moqui-lexicon-audit.js \
60
96
  --fail-on-gap \
61
97
  --json
62
98
 
99
+ # 0.4) Consolidated release gate summary (single-file pass/fail/incomplete verdict)
100
+ node scripts/moqui-release-summary.js \
101
+ --fail-on-gate-fail \
102
+ --json
103
+
63
104
  # 1) Handoff close-loop
64
105
  sce auto handoff run --manifest docs/handoffs/handoff-manifest.json --json
65
106
 
@@ -99,6 +140,8 @@ Required artifacts for each intake batch:
99
140
  - `.kiro/reports/release-evidence/moqui-capability-coverage.md`
100
141
  - `.kiro/reports/release-evidence/moqui-lexicon-audit.json`
101
142
  - `.kiro/reports/release-evidence/moqui-lexicon-audit.md`
143
+ - `.kiro/reports/release-evidence/moqui-release-summary.json`
144
+ - `.kiro/reports/release-evidence/moqui-release-summary.md`
102
145
  - `.kiro/reports/handoff-capability-matrix.md` (or JSON equivalent from `sce auto handoff capability-matrix`)
103
146
  - `.kiro/reports/handoff-runs/<session>.json`
104
147
  - `.kiro/reports/scene-package-ontology-batch.json`
@@ -4701,6 +4701,67 @@ function buildBatchMetrics(results, totalGoals) {
4701
4701
  };
4702
4702
  }
4703
4703
 
4704
+ function applyAdaptiveRateLimitBackpressure({
4705
+ currentBatchParallel,
4706
+ currentBatchAgentBudget,
4707
+ rateLimitSignals,
4708
+ rateLimitBackoffMs,
4709
+ rateLimitLaunchHoldMs,
4710
+ inputGoals
4711
+ }) {
4712
+ const next = {
4713
+ batchParallel: Number.isInteger(currentBatchParallel) ? currentBatchParallel : 1,
4714
+ batchAgentBudget: currentBatchAgentBudget,
4715
+ applied: false,
4716
+ level: 'none',
4717
+ signalsPerGoal: 0
4718
+ };
4719
+
4720
+ const safeGoals = Math.max(1, Number(inputGoals) || 1);
4721
+ const normalizedSignals = Math.max(0, Number(rateLimitSignals) || 0);
4722
+ const normalizedBackoffMs = Math.max(0, Number(rateLimitBackoffMs) || 0);
4723
+ const normalizedLaunchHoldMs = Math.max(0, Number(rateLimitLaunchHoldMs) || 0);
4724
+ const signalsPerGoal = normalizedSignals / safeGoals;
4725
+ next.signalsPerGoal = Number(signalsPerGoal.toFixed(2));
4726
+
4727
+ if (normalizedSignals <= 0 && normalizedBackoffMs <= 0 && normalizedLaunchHoldMs <= 0) {
4728
+ return next;
4729
+ }
4730
+
4731
+ const severePressure = (
4732
+ signalsPerGoal >= 1.5 ||
4733
+ normalizedBackoffMs >= 4000 ||
4734
+ normalizedLaunchHoldMs >= 2000
4735
+ );
4736
+ next.level = severePressure ? 'severe' : 'mild';
4737
+
4738
+ const normalizePositiveInteger = (value, fallback = 1) => {
4739
+ const parsed = Number(value);
4740
+ if (!Number.isInteger(parsed) || parsed < 1) {
4741
+ return fallback;
4742
+ }
4743
+ return parsed;
4744
+ };
4745
+
4746
+ const currentParallel = normalizePositiveInteger(next.batchParallel, 1);
4747
+ const reducedParallel = severePressure
4748
+ ? Math.max(1, Math.floor(currentParallel / 2))
4749
+ : Math.max(1, currentParallel - 1);
4750
+ next.batchParallel = reducedParallel;
4751
+ next.applied = reducedParallel !== currentParallel;
4752
+
4753
+ if (currentBatchAgentBudget !== null && currentBatchAgentBudget !== undefined) {
4754
+ const currentBudget = normalizePositiveInteger(currentBatchAgentBudget, 1);
4755
+ const reducedBudget = severePressure
4756
+ ? Math.max(1, Math.floor(currentBudget / 2))
4757
+ : Math.max(1, currentBudget - 1);
4758
+ next.batchAgentBudget = reducedBudget;
4759
+ next.applied = next.applied || reducedBudget !== currentBudget;
4760
+ }
4761
+
4762
+ return next;
4763
+ }
4764
+
4704
4765
  async function runCloseLoopBatchGoals(goals, options) {
4705
4766
  const continueOnError = Boolean(options.continueOnError);
4706
4767
  const batchParallel = normalizeBatchParallel(options.batchParallel);
@@ -4996,16 +5057,25 @@ async function runCloseLoopBatchWithRetries(goals, options) {
4996
5057
  let nextBatchParallel = adaptiveBatchParallel;
4997
5058
  let nextBatchAgentBudget = adaptiveBatchAgentBudget;
4998
5059
  let adaptiveBackpressureApplied = false;
5060
+ let backpressureLevel = 'none';
5061
+ let roundRateLimitSignalsPerGoal = Number(
5062
+ (roundRateLimitSignals / Math.max(1, pendingEntries.length)).toFixed(2)
5063
+ );
4999
5064
  const hasRateLimitPressure = roundRateLimitSignals > 0 || roundRateLimitBackoffMs > 0 || roundMaxLaunchHoldMs > 0;
5000
5065
  if (retryStrategy === 'adaptive' && retryEntries.length > 0 && hasRateLimitPressure) {
5001
- if (nextBatchParallel > 1) {
5002
- nextBatchParallel -= 1;
5003
- adaptiveBackpressureApplied = true;
5004
- }
5005
- if (nextBatchAgentBudget !== null && nextBatchAgentBudget > 1) {
5006
- nextBatchAgentBudget -= 1;
5007
- adaptiveBackpressureApplied = true;
5008
- }
5066
+ const backpressure = applyAdaptiveRateLimitBackpressure({
5067
+ currentBatchParallel: adaptiveBatchParallel,
5068
+ currentBatchAgentBudget: adaptiveBatchAgentBudget,
5069
+ rateLimitSignals: roundRateLimitSignals,
5070
+ rateLimitBackoffMs: roundRateLimitBackoffMs,
5071
+ rateLimitLaunchHoldMs: roundMaxLaunchHoldMs,
5072
+ inputGoals: pendingEntries.length
5073
+ });
5074
+ nextBatchParallel = backpressure.batchParallel;
5075
+ nextBatchAgentBudget = backpressure.batchAgentBudget;
5076
+ adaptiveBackpressureApplied = backpressure.applied;
5077
+ backpressureLevel = backpressure.level;
5078
+ roundRateLimitSignalsPerGoal = backpressure.signalsPerGoal;
5009
5079
  }
5010
5080
 
5011
5081
  retryHistory.push({
@@ -5019,9 +5089,11 @@ async function runCloseLoopBatchWithRetries(goals, options) {
5019
5089
  unprocessed_goals: unprocessedCount,
5020
5090
  stopped_early: Boolean(run.stoppedEarly),
5021
5091
  rate_limit_signals: roundRateLimitSignals,
5092
+ rate_limit_signals_per_goal: roundRateLimitSignalsPerGoal,
5022
5093
  rate_limit_backoff_ms: roundRateLimitBackoffMs,
5023
5094
  rate_limit_launch_hold_ms: roundMaxLaunchHoldMs,
5024
5095
  adaptive_backpressure_applied: adaptiveBackpressureApplied,
5096
+ backpressure_level: backpressureLevel,
5025
5097
  next_batch_parallel: nextBatchParallel,
5026
5098
  next_batch_agent_budget: nextBatchAgentBudget
5027
5099
  });
@@ -107,6 +107,8 @@ class OrchestrationEngine extends EventEmitter {
107
107
  this._launchBudgetLastHoldSignalAt = 0;
108
108
  /** @type {number} last launch-budget hold duration emitted to telemetry (ms) */
109
109
  this._launchBudgetLastHoldMs = 0;
110
+ /** @type {Set<{timer: NodeJS.Timeout|null, resolve: (() => void)|null}>} cancellable sleep waiters */
111
+ this._pendingSleeps = new Set();
110
112
  /** @type {number} fallback wait timeout to avoid indefinite hangs when lifecycle events are missing */
111
113
  this._agentWaitTimeoutMs = (DEFAULT_AGENT_WAIT_TIMEOUT_SECONDS * 1000) + AGENT_WAIT_TIMEOUT_GRACE_MS;
112
114
  /** @type {() => number} */
@@ -230,6 +232,7 @@ class OrchestrationEngine extends EventEmitter {
230
232
  */
231
233
  async stop() {
232
234
  this._stopped = true;
235
+ this._cancelPendingSleeps();
233
236
 
234
237
  if (this._state !== 'running') {
235
238
  return;
@@ -520,10 +523,7 @@ class OrchestrationEngine extends EventEmitter {
520
523
  this._statusMonitor.updateSpecStatus(specName, 'pending', null, resolvedError);
521
524
 
522
525
  const retryDelayMs = isRateLimitError
523
- ? Math.max(
524
- this._calculateRateLimitBackoffMs(retryCount),
525
- this._extractRateLimitRetryAfterMs(resolvedError)
526
- )
526
+ ? this._resolveRateLimitRetryDelayMs(resolvedError, retryCount)
527
527
  : 0;
528
528
  if (retryDelayMs > 0) {
529
529
  this._onRateLimitSignal(retryDelayMs);
@@ -1156,16 +1156,73 @@ class OrchestrationEngine extends EventEmitter {
1156
1156
  return Math.max(1, Math.round(cappedBaseDelay * jitterFactor));
1157
1157
  }
1158
1158
 
1159
+ /**
1160
+ * Resolve final retry delay for rate-limit failures.
1161
+ * Uses larger of computed backoff and retry-after hint, then clamps to configured max.
1162
+ *
1163
+ * @param {string} error
1164
+ * @param {number} retryCount
1165
+ * @returns {number}
1166
+ * @private
1167
+ */
1168
+ _resolveRateLimitRetryDelayMs(error, retryCount) {
1169
+ const computedBackoffMs = this._calculateRateLimitBackoffMs(retryCount);
1170
+ const hintedRetryAfterMs = this._extractRateLimitRetryAfterMs(error);
1171
+ const candidateDelayMs = Math.max(computedBackoffMs, hintedRetryAfterMs);
1172
+ const maxDelayMs = this._toPositiveInteger(
1173
+ this._rateLimitBackoffMaxMs,
1174
+ DEFAULT_RATE_LIMIT_BACKOFF_MAX_MS
1175
+ );
1176
+ return Math.max(1, Math.min(candidateDelayMs, maxDelayMs));
1177
+ }
1178
+
1159
1179
  /**
1160
1180
  * @param {number} ms
1161
1181
  * @returns {Promise<void>}
1162
1182
  * @private
1163
1183
  */
1164
1184
  _sleep(ms) {
1165
- if (!ms || ms <= 0) {
1185
+ if (!ms || ms <= 0 || this._stopped) {
1166
1186
  return Promise.resolve();
1167
1187
  }
1168
- return new Promise((resolve) => setTimeout(resolve, ms));
1188
+ return new Promise((resolve) => {
1189
+ let settled = false;
1190
+ const entry = { timer: null, resolve: null };
1191
+ entry.resolve = () => {
1192
+ if (settled) {
1193
+ return;
1194
+ }
1195
+ settled = true;
1196
+ if (entry.timer) {
1197
+ clearTimeout(entry.timer);
1198
+ }
1199
+ this._pendingSleeps.delete(entry);
1200
+ resolve();
1201
+ };
1202
+ entry.timer = setTimeout(() => {
1203
+ if (entry.resolve) {
1204
+ entry.resolve();
1205
+ }
1206
+ }, ms);
1207
+ this._pendingSleeps.add(entry);
1208
+ });
1209
+ }
1210
+
1211
+ /**
1212
+ * Cancel all pending sleeps so stop() does not block behind long retry waits.
1213
+ *
1214
+ * @private
1215
+ */
1216
+ _cancelPendingSleeps() {
1217
+ if (!this._pendingSleeps || this._pendingSleeps.size === 0) {
1218
+ return;
1219
+ }
1220
+ for (const entry of Array.from(this._pendingSleeps)) {
1221
+ if (entry && typeof entry.resolve === 'function') {
1222
+ entry.resolve();
1223
+ }
1224
+ }
1225
+ this._pendingSleeps.clear();
1169
1226
  }
1170
1227
 
1171
1228
  /**
@@ -1300,6 +1357,7 @@ class OrchestrationEngine extends EventEmitter {
1300
1357
  * @private
1301
1358
  */
1302
1359
  _reset() {
1360
+ this._cancelPendingSleeps();
1303
1361
  this._runningAgents.clear();
1304
1362
  this._retryCounts.clear();
1305
1363
  this._failedSpecs.clear();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scene-capability-engine",
3
- "version": "3.0.6",
3
+ "version": "3.0.8",
4
4
  "description": "SCE (Scene Capability Engine) - A CLI tool and npm package for spec-driven development with AI coding assistants.",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -35,7 +35,11 @@
35
35
  "test:brand-consistency": "node scripts/check-branding-consistency.js",
36
36
  "test:watch": "npx jest --watch",
37
37
  "coverage": "npx jest --coverage",
38
+ "report:moqui-metadata": "node scripts/moqui-metadata-extract.js --json",
39
+ "report:moqui-rebuild": "node scripts/moqui-standard-rebuild.js --json",
40
+ "gate:moqui-rebuild": "node scripts/moqui-rebuild-gate.js",
38
41
  "report:moqui-baseline": "node scripts/moqui-template-baseline-report.js --json",
42
+ "report:moqui-summary": "node scripts/moqui-release-summary.js --json",
39
43
  "report:moqui-core-regression": "node scripts/moqui-core-regression-suite.js --json",
40
44
  "prepublishOnly": "npm run test:full && npm run test:skip-audit && npm run test:brand-consistency",
41
45
  "publish:manual": "npm publish --access public",