eyeling 1.21.2 → 1.21.4
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/HANDBOOK.md +65 -0
- package/README.md +0 -1
- package/dist/browser/eyeling.browser.js +12 -1
- package/examples/arc-bridge/README.md +208 -0
- package/examples/arc-bridge/delfour/delfour.data.json +68 -0
- package/examples/arc-bridge/delfour/delfour.expected.json +88 -0
- package/examples/arc-bridge/delfour/delfour.instance.schema.json +201 -0
- package/examples/arc-bridge/delfour/delfour.model.mjs +273 -0
- package/examples/arc-bridge/delfour/delfour.spec.md +118 -0
- package/examples/arc-bridge/flandor/flandor.data.json +107 -0
- package/examples/arc-bridge/flandor/flandor.expected.json +98 -0
- package/examples/arc-bridge/flandor/flandor.instance.schema.json +285 -0
- package/examples/arc-bridge/flandor/flandor.model.mjs +303 -0
- package/examples/arc-bridge/flandor/flandor.spec.md +156 -0
- package/examples/extra/flandor.js +349 -0
- package/examples/extra/output/flandor.txt +31 -0
- package/examples/flandor.n3 +425 -0
- package/examples/output/flandor.n3 +31 -0
- package/eyeling.js +12 -1
- package/lib/builtins.js +12 -1
- package/package.json +1 -2
- package/test/api.test.js +49 -0
- package/SEMANTICS.md +0 -41
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# Flandor — ARC Specification
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
This document is the **normative specification** for the Flandor case. The file `flandor.model.mjs` is the **reference ECMAScript implementation** of these clauses. The file `flandor.data.json` is the **instance** evaluated in this bundle. The file `flandor.expected.json` is the **conformance vector** for that instance.
|
|
6
|
+
|
|
7
|
+
## Aha
|
|
8
|
+
|
|
9
|
+
Nobody has to reveal their books for the region to coordinate.
|
|
10
|
+
|
|
11
|
+
Firm-side, labour-side, and grid-side evidence remain local. What crosses the policy boundary is a narrow, signed, expiring conclusion: Flanders presently faces enough combined pressure to justify a temporary retooling response. The traded product is not raw data, but a permissioned conclusion.
|
|
12
|
+
|
|
13
|
+
## Conventions
|
|
14
|
+
|
|
15
|
+
- “iff” means “if and only if”.
|
|
16
|
+
- A clause identifier such as `R1` or `M3` is normative.
|
|
17
|
+
- A conforming implementation may be written in any language, but it shall produce the same derived values and pass/fail outcomes for the supplied instance.
|
|
18
|
+
- The reference implementation uses ECMAScript because you preferred an international-standard JS language.
|
|
19
|
+
|
|
20
|
+
## Vocabulary
|
|
21
|
+
|
|
22
|
+
**V1. Region** is the polity for which a macro-economic insight is derived.
|
|
23
|
+
|
|
24
|
+
**V2. Industrial cluster** is a regional production grouping with an export-orders index.
|
|
25
|
+
|
|
26
|
+
**V3. Labour-market signal** is a regional indicator that includes the technical vacancy rate.
|
|
27
|
+
|
|
28
|
+
**V4. Grid signal** is a regional indicator that includes congestion hours.
|
|
29
|
+
|
|
30
|
+
**V5. Policy package** is an intervention option with cost and coverage properties.
|
|
31
|
+
|
|
32
|
+
**V6. Insight envelope** is the ordered pair `(insight, policy)` together with integrity metadata.
|
|
33
|
+
|
|
34
|
+
## Input instance
|
|
35
|
+
|
|
36
|
+
**I1.** The region is `Flanders`.
|
|
37
|
+
|
|
38
|
+
**I2.** The observed clusters are Antwerp chemicals and Ghent manufacturing.
|
|
39
|
+
|
|
40
|
+
**I3.** The technical vacancy rate is `4.6%`.
|
|
41
|
+
|
|
42
|
+
**I4.** Grid congestion is `19` hours.
|
|
43
|
+
|
|
44
|
+
**I5.** The budget cap is `€140M`.
|
|
45
|
+
|
|
46
|
+
**I6.** The candidate packages are the four packages listed in `flandor.data.json`.
|
|
47
|
+
|
|
48
|
+
## Derivation clauses
|
|
49
|
+
|
|
50
|
+
**R1. ExportWeakness.**
|
|
51
|
+
`ExportWeakness` holds iff there exists a cluster `c` such that `c.exportOrdersIndex < 90`.
|
|
52
|
+
|
|
53
|
+
**R2. SkillsStrain.**
|
|
54
|
+
`SkillsStrain` holds iff `technicalVacancyRatePct > 3.9`.
|
|
55
|
+
|
|
56
|
+
**R3. GridStress.**
|
|
57
|
+
`GridStress` holds iff `congestionHours > 11`.
|
|
58
|
+
|
|
59
|
+
**R4. ActiveNeedCount.**
|
|
60
|
+
`ActiveNeedCount` is the number of true predicates among `ExportWeakness`, `SkillsStrain`, and `GridStress`.
|
|
61
|
+
|
|
62
|
+
**R5. NeedsRetoolingPulse.**
|
|
63
|
+
`NeedsRetoolingPulse` holds iff `ActiveNeedCount ≥ 3`.
|
|
64
|
+
|
|
65
|
+
## Selection clauses
|
|
66
|
+
|
|
67
|
+
**S1. Eligible(p).**
|
|
68
|
+
A package `p` is eligible iff:
|
|
69
|
+
|
|
70
|
+
1. `p.costMEUR ≤ budget.maxMEUR`; and
|
|
71
|
+
2. for every active need, `p` covers that need.
|
|
72
|
+
|
|
73
|
+
**S2. RecommendedPackage.**
|
|
74
|
+
`RecommendedPackage` is the eligible package with minimum `costMEUR`.
|
|
75
|
+
|
|
76
|
+
**S3. No-package fallback.**
|
|
77
|
+
If no eligible package exists, the recommendation is `None`.
|
|
78
|
+
|
|
79
|
+
## Governance clauses
|
|
80
|
+
|
|
81
|
+
**G1. AuthorizedUse.**
|
|
82
|
+
`AuthorizedUse` holds iff:
|
|
83
|
+
|
|
84
|
+
1. the requested action is `odrl:use`;
|
|
85
|
+
2. the requested purpose is `regional_stabilization`; and
|
|
86
|
+
3. the authorization time is not later than the expiry time.
|
|
87
|
+
|
|
88
|
+
**G2. SurveillanceReuseProhibited.**
|
|
89
|
+
`SurveillanceReuseProhibited` holds iff the policy prohibits distribution for purpose `firm_surveillance`.
|
|
90
|
+
|
|
91
|
+
**G3. DutyTimely.**
|
|
92
|
+
`DutyTimely` holds iff the duty-performance time is not later than the expiry time.
|
|
93
|
+
|
|
94
|
+
## Integrity and minimization clauses
|
|
95
|
+
|
|
96
|
+
**M1. CanonicalEnvelope.**
|
|
97
|
+
The canonical envelope string is the stable JSON serialization of the ordered pair `(insight, policy)`, with object keys sorted lexicographically at every level.
|
|
98
|
+
|
|
99
|
+
**M2. PayloadHashMatches.**
|
|
100
|
+
`PayloadHashMatches` holds iff `SHA-256(CanonicalEnvelope) = declaredPayloadHashSHA256`.
|
|
101
|
+
|
|
102
|
+
**M3. SignatureVerifies.**
|
|
103
|
+
`SignatureVerifies` holds iff the declared HMAC verifies under the agreed verification mode.
|
|
104
|
+
|
|
105
|
+
**M4. MinimizationRespected.**
|
|
106
|
+
`MinimizationRespected` holds iff the serialized insight contains none of the forbidden terms: `salary`, `payroll`, `invoice`, `medical`, `firmname`.
|
|
107
|
+
|
|
108
|
+
**M5. ScopeComplete.**
|
|
109
|
+
`ScopeComplete` holds iff the insight contains `scopeDevice`, `scopeEvent`, and `expiresAt`.
|
|
110
|
+
|
|
111
|
+
## Output contract
|
|
112
|
+
|
|
113
|
+
**O1. Answer.**
|
|
114
|
+
A conforming renderer shall expose:
|
|
115
|
+
|
|
116
|
+
- case name
|
|
117
|
+
- region
|
|
118
|
+
- metric
|
|
119
|
+
- active need count
|
|
120
|
+
- threshold
|
|
121
|
+
- recommended package
|
|
122
|
+
- budget cap
|
|
123
|
+
- package cost
|
|
124
|
+
- payload hash
|
|
125
|
+
- envelope HMAC
|
|
126
|
+
|
|
127
|
+
**O2. Reason Why.**
|
|
128
|
+
A conforming renderer shall explain which predicates hold and why the package was selected.
|
|
129
|
+
|
|
130
|
+
**O3. Check.**
|
|
131
|
+
A conforming renderer shall expose a named PASS/FAIL outcome for each of:
|
|
132
|
+
|
|
133
|
+
- payloadHashMatches
|
|
134
|
+
- signatureVerifies
|
|
135
|
+
- thresholdReached
|
|
136
|
+
- scopeComplete
|
|
137
|
+
- minimizationRespected
|
|
138
|
+
- authorizationAllowed
|
|
139
|
+
- dutyTimely
|
|
140
|
+
- surveillanceReuseProhibited
|
|
141
|
+
- packageWithinBudget
|
|
142
|
+
- packageCoversAllActiveNeeds
|
|
143
|
+
- lowestCostEligiblePackageChosen
|
|
144
|
+
|
|
145
|
+
## Reference outcome for this instance
|
|
146
|
+
|
|
147
|
+
For the supplied instance:
|
|
148
|
+
|
|
149
|
+
- `ExportWeakness = true`
|
|
150
|
+
- `SkillsStrain = true`
|
|
151
|
+
- `GridStress = true`
|
|
152
|
+
- `ActiveNeedCount = 3`
|
|
153
|
+
- `NeedsRetoolingPulse = true`
|
|
154
|
+
- `RecommendedPackage = "Flandor Retooling Pulse"`
|
|
155
|
+
|
|
156
|
+
The expected ARC report and integrity values are recorded in `flandor.expected.json`.
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Flandor — a macro-economic Insight Economy case for Flanders.
|
|
7
|
+
*
|
|
8
|
+
* The aha: nobody has to reveal their books for the region to coordinate.
|
|
9
|
+
*
|
|
10
|
+
* Exporters, training actors, and grid operators each keep their sensitive
|
|
11
|
+
* data local. What crosses the boundary is not the underlying evidence, but a
|
|
12
|
+
* narrow, signed, expiring insight: right now, Flanders has enough combined
|
|
13
|
+
* pressure to justify a temporary retooling response.
|
|
14
|
+
*
|
|
15
|
+
* That is the Insight Economy in action. Confidential micro-signals are
|
|
16
|
+
* transformed into a macro decision object that is:
|
|
17
|
+
* - useful enough to trigger action,
|
|
18
|
+
* - minimal enough to protect competitive and operational secrets,
|
|
19
|
+
* - governed enough to say who may use it, for what purpose, and until when.
|
|
20
|
+
*
|
|
21
|
+
* In this case, the insight says that export weakness, technical labour
|
|
22
|
+
* scarcity, and grid congestion together clear the threshold for a temporary
|
|
23
|
+
* industrial retooling package. Policymakers can act on that conclusion
|
|
24
|
+
* without gaining access to firm-level margins, vacancy lists, or grid-control
|
|
25
|
+
* details.
|
|
26
|
+
*
|
|
27
|
+
* The product being traded is therefore not raw data, and not even a general
|
|
28
|
+
* forecast, but a context-bound permissioned conclusion: a policy-grade
|
|
29
|
+
* insight for regional stabilization, with reuse for firm surveillance
|
|
30
|
+
* explicitly forbidden.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
const crypto = require('node:crypto');
|
|
34
|
+
|
|
35
|
+
const SECRET = 'flandor-demo-shared-secret';
|
|
36
|
+
|
|
37
|
+
const HUB_CREATED_AT = '2026-04-08T07:00:00+00:00';
|
|
38
|
+
const HUB_EXPIRES_AT = '2026-04-08T19:00:00+00:00';
|
|
39
|
+
const BOARD_AUTH_AT = '2026-04-08T09:15:00+00:00';
|
|
40
|
+
const BOARD_DUTY_AT = '2026-04-08T18:30:00+00:00';
|
|
41
|
+
|
|
42
|
+
const REGION = 'Flanders';
|
|
43
|
+
|
|
44
|
+
const CLUSTERS = [
|
|
45
|
+
{ id: 'cluster:ANT_CHEM', name: 'Antwerp chemicals', exportOrdersIndex: 84, energyIntensity: 92 },
|
|
46
|
+
{ id: 'cluster:GNT_MFG', name: 'Ghent manufacturing', exportOrdersIndex: 87, energyIntensity: 76 },
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
const LABOUR_MARKET = {
|
|
50
|
+
technicalVacancyRateTenths: 46,
|
|
51
|
+
technicalVacancyRatePct: 4.6,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const GRID = {
|
|
55
|
+
congestionHours: 19,
|
|
56
|
+
renewableCurtailmentMWh: 240,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const BUDGET = {
|
|
60
|
+
windowName: 'Q2 resilience window',
|
|
61
|
+
maxMEUR: 140,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const PACKAGES = [
|
|
65
|
+
{
|
|
66
|
+
id: 'pkg:TRAIN_070',
|
|
67
|
+
name: 'Flanders Skills Sprint',
|
|
68
|
+
costMEUR: 70,
|
|
69
|
+
workerCoverage: 900,
|
|
70
|
+
gridReliefMW: 0,
|
|
71
|
+
coversExportWeakness: false,
|
|
72
|
+
coversSkillsStrain: true,
|
|
73
|
+
coversGridStress: false,
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
id: 'pkg:PORT_095',
|
|
77
|
+
name: 'Schelde Trade Buffer',
|
|
78
|
+
costMEUR: 95,
|
|
79
|
+
workerCoverage: 300,
|
|
80
|
+
gridReliefMW: 10,
|
|
81
|
+
coversExportWeakness: true,
|
|
82
|
+
coversSkillsStrain: false,
|
|
83
|
+
coversGridStress: false,
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
id: 'pkg:RET_FLEX_120',
|
|
87
|
+
name: 'Flandor Retooling Pulse',
|
|
88
|
+
costMEUR: 120,
|
|
89
|
+
workerCoverage: 1200,
|
|
90
|
+
gridReliefMW: 85,
|
|
91
|
+
coversExportWeakness: true,
|
|
92
|
+
coversSkillsStrain: true,
|
|
93
|
+
coversGridStress: true,
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
id: 'pkg:CORRIDOR_165',
|
|
97
|
+
name: 'Full Corridor Shock Shield',
|
|
98
|
+
costMEUR: 165,
|
|
99
|
+
workerCoverage: 1600,
|
|
100
|
+
gridReliefMW: 110,
|
|
101
|
+
coversExportWeakness: true,
|
|
102
|
+
coversSkillsStrain: true,
|
|
103
|
+
coversGridStress: true,
|
|
104
|
+
},
|
|
105
|
+
];
|
|
106
|
+
|
|
107
|
+
function sha256Hex(text) {
|
|
108
|
+
return crypto.createHash('sha256').update(text).digest('hex');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function hmacSha256Hex(secret, text) {
|
|
112
|
+
return crypto.createHmac('sha256', secret).update(text).digest('hex');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function countTrue(values) {
|
|
116
|
+
return values.reduce((sum, value) => sum + (value ? 1 : 0), 0);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function packageCoversAllNeeds(pkg, needs) {
|
|
120
|
+
return (
|
|
121
|
+
(!needs.exportWeakness || pkg.coversExportWeakness) &&
|
|
122
|
+
(!needs.skillsStrain || pkg.coversSkillsStrain) &&
|
|
123
|
+
(!needs.gridStress || pkg.coversGridStress)
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function chooseLowestCostCoveringPackage(packages, needs, maxMEUR) {
|
|
128
|
+
const eligible = packages
|
|
129
|
+
.filter((pkg) => pkg.costMEUR <= maxMEUR)
|
|
130
|
+
.filter((pkg) => packageCoversAllNeeds(pkg, needs))
|
|
131
|
+
.sort((a, b) => a.costMEUR - b.costMEUR);
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
eligible,
|
|
135
|
+
recommended: eligible[0] || null,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function runDemo() {
|
|
140
|
+
const exportWeakness = CLUSTERS.some((cluster) => cluster.exportOrdersIndex < 90);
|
|
141
|
+
const skillsStrain = LABOUR_MARKET.technicalVacancyRateTenths > 39;
|
|
142
|
+
const gridStress = GRID.congestionHours > 11;
|
|
143
|
+
|
|
144
|
+
const activeNeedCount = countTrue([exportWeakness, skillsStrain, gridStress]);
|
|
145
|
+
|
|
146
|
+
const insightId = 'https://example.org/insight/flandor';
|
|
147
|
+
const insight = {
|
|
148
|
+
createdAt: HUB_CREATED_AT,
|
|
149
|
+
expiresAt: HUB_EXPIRES_AT,
|
|
150
|
+
id: insightId,
|
|
151
|
+
metric: 'regional_retooling_priority',
|
|
152
|
+
region: REGION,
|
|
153
|
+
scopeDevice: 'economic-resilience-board',
|
|
154
|
+
scopeEvent: 'budget-prep-window',
|
|
155
|
+
suggestionPolicy: 'lowest_cost_package_covering_all_active_needs',
|
|
156
|
+
threshold: 3,
|
|
157
|
+
type: 'ins:Insight',
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const policy = {
|
|
161
|
+
duty: {
|
|
162
|
+
action: 'odrl:delete',
|
|
163
|
+
constraint: {
|
|
164
|
+
leftOperand: 'odrl:dateTime',
|
|
165
|
+
operator: 'odrl:eq',
|
|
166
|
+
rightOperand: HUB_EXPIRES_AT,
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
permission: {
|
|
170
|
+
action: 'odrl:use',
|
|
171
|
+
constraint: {
|
|
172
|
+
leftOperand: 'odrl:purpose',
|
|
173
|
+
operator: 'odrl:eq',
|
|
174
|
+
rightOperand: 'regional_stabilization',
|
|
175
|
+
},
|
|
176
|
+
target: insightId,
|
|
177
|
+
},
|
|
178
|
+
profile: 'Flandor-Insight-Policy',
|
|
179
|
+
prohibition: {
|
|
180
|
+
action: 'odrl:distribute',
|
|
181
|
+
constraint: {
|
|
182
|
+
leftOperand: 'odrl:purpose',
|
|
183
|
+
operator: 'odrl:eq',
|
|
184
|
+
rightOperand: 'firm_surveillance',
|
|
185
|
+
},
|
|
186
|
+
target: insightId,
|
|
187
|
+
},
|
|
188
|
+
type: 'odrl:Policy',
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
const envelope = { insight, policy };
|
|
192
|
+
const insightJson = JSON.stringify(insight);
|
|
193
|
+
const policyJson = JSON.stringify(policy);
|
|
194
|
+
const envelopeJson = JSON.stringify(envelope);
|
|
195
|
+
|
|
196
|
+
const payloadHashHex = sha256Hex(envelopeJson);
|
|
197
|
+
const hmacHex = hmacSha256Hex(SECRET, envelopeJson);
|
|
198
|
+
|
|
199
|
+
const checkHash = sha256Hex(envelopeJson);
|
|
200
|
+
const checkHmac = hmacSha256Hex(SECRET, envelopeJson);
|
|
201
|
+
|
|
202
|
+
const needs = { exportWeakness, skillsStrain, gridStress };
|
|
203
|
+
const choice = chooseLowestCostCoveringPackage(PACKAGES, needs, BUDGET.maxMEUR);
|
|
204
|
+
const recommended = choice.recommended;
|
|
205
|
+
|
|
206
|
+
const scopeComplete =
|
|
207
|
+
insightJson.includes('scopeDevice') && insightJson.includes('scopeEvent') && insightJson.includes('expiresAt');
|
|
208
|
+
|
|
209
|
+
const minimizationOk =
|
|
210
|
+
!insightJson.includes('firmName') &&
|
|
211
|
+
!insightJson.includes('payroll') &&
|
|
212
|
+
!insightJson.includes('salary') &&
|
|
213
|
+
!insightJson.includes('invoice') &&
|
|
214
|
+
!insightJson.includes('medical');
|
|
215
|
+
|
|
216
|
+
const authorizationAllowed =
|
|
217
|
+
BOARD_AUTH_AT < HUB_EXPIRES_AT && policy.permission.constraint.rightOperand === 'regional_stabilization';
|
|
218
|
+
|
|
219
|
+
const dutyTimingOk = BOARD_DUTY_AT <= HUB_EXPIRES_AT;
|
|
220
|
+
|
|
221
|
+
const surveillanceReuseProhibited =
|
|
222
|
+
policy.prohibition.action === 'odrl:distribute' &&
|
|
223
|
+
policy.prohibition.constraint.rightOperand === 'firm_surveillance';
|
|
224
|
+
|
|
225
|
+
const packageWithinBudget = Boolean(recommended) && recommended.costMEUR <= BUDGET.maxMEUR;
|
|
226
|
+
const packageCoversNeeds = Boolean(recommended) && packageCoversAllNeeds(recommended, needs);
|
|
227
|
+
const cheapestEligibleChosen =
|
|
228
|
+
Boolean(recommended) && choice.eligible.length > 0 && recommended.id === choice.eligible[0].id;
|
|
229
|
+
|
|
230
|
+
return {
|
|
231
|
+
exportWeakness,
|
|
232
|
+
skillsStrain,
|
|
233
|
+
gridStress,
|
|
234
|
+
activeNeedCount,
|
|
235
|
+
insight,
|
|
236
|
+
policy,
|
|
237
|
+
insightJson,
|
|
238
|
+
policyJson,
|
|
239
|
+
envelopeJson,
|
|
240
|
+
payloadHashHex,
|
|
241
|
+
hmacHex,
|
|
242
|
+
checkHash,
|
|
243
|
+
checkHmac,
|
|
244
|
+
needs,
|
|
245
|
+
choice,
|
|
246
|
+
recommended,
|
|
247
|
+
scopeComplete,
|
|
248
|
+
minimizationOk,
|
|
249
|
+
authorizationAllowed,
|
|
250
|
+
dutyTimingOk,
|
|
251
|
+
surveillanceReuseProhibited,
|
|
252
|
+
packageWithinBudget,
|
|
253
|
+
packageCoversNeeds,
|
|
254
|
+
cheapestEligibleChosen,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function renderAnswer(state) {
|
|
259
|
+
const lines = [];
|
|
260
|
+
lines.push('=== Answer ===');
|
|
261
|
+
lines.push(`Name: Flandor`);
|
|
262
|
+
lines.push(`Region: ${state.insight.region}`);
|
|
263
|
+
lines.push(`Metric: ${state.insight.metric}`);
|
|
264
|
+
lines.push(`Active need count: ${state.activeNeedCount}/${state.insight.threshold}`);
|
|
265
|
+
lines.push(`Recommended package: ${state.recommended ? state.recommended.name : 'none'}`);
|
|
266
|
+
lines.push(`Budget cap: €${BUDGET.maxMEUR}M`);
|
|
267
|
+
lines.push(`Package cost: €${state.recommended ? state.recommended.costMEUR : 'n/a'}M`);
|
|
268
|
+
lines.push(`Payload SHA-256: ${state.payloadHashHex}`);
|
|
269
|
+
lines.push(`Envelope HMAC-SHA-256: ${state.hmacHex}`);
|
|
270
|
+
return lines.join('\n');
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function renderReasonWhy(state) {
|
|
274
|
+
const lines = [];
|
|
275
|
+
lines.push('=== Reason Why ===');
|
|
276
|
+
lines.push(
|
|
277
|
+
`Export weakness is active because at least one cluster has exportOrdersIndex < 90 ` +
|
|
278
|
+
`(${CLUSTERS.map((c) => `${c.name}=${c.exportOrdersIndex}`).join(', ')}).`,
|
|
279
|
+
);
|
|
280
|
+
lines.push(
|
|
281
|
+
`Skills strain is active because technical vacancy rate is ${LABOUR_MARKET.technicalVacancyRatePct}% ` +
|
|
282
|
+
`(threshold > 3.9%).`,
|
|
283
|
+
);
|
|
284
|
+
lines.push(`Grid stress is active because congestion hours = ${GRID.congestionHours} ` + `(threshold > 11).`);
|
|
285
|
+
lines.push(
|
|
286
|
+
`The recommendation policy is "${state.insight.suggestionPolicy}", so the cheapest package that ` +
|
|
287
|
+
`covers all active needs within budget is selected.`,
|
|
288
|
+
);
|
|
289
|
+
if (state.recommended) {
|
|
290
|
+
lines.push(
|
|
291
|
+
`Selected package "${state.recommended.name}" covers export=${state.recommended.coversExportWeakness}, ` +
|
|
292
|
+
`skills=${state.recommended.coversSkillsStrain}, grid=${state.recommended.coversGridStress}, ` +
|
|
293
|
+
`cost=€${state.recommended.costMEUR}M.`,
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
lines.push(
|
|
297
|
+
`Usage is permitted only for purpose "${state.policy.permission.constraint.rightOperand}" and ` +
|
|
298
|
+
`the envelope expires at ${state.insight.expiresAt}.`,
|
|
299
|
+
);
|
|
300
|
+
return lines.join('\n');
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function renderCheck(state) {
|
|
304
|
+
const checks = [
|
|
305
|
+
['payload hash matches', state.checkHash === state.payloadHashHex],
|
|
306
|
+
['hmac matches', state.checkHmac === state.hmacHex],
|
|
307
|
+
['threshold reached', state.activeNeedCount >= state.insight.threshold],
|
|
308
|
+
['scope complete', state.scopeComplete],
|
|
309
|
+
['minimization respected', state.minimizationOk],
|
|
310
|
+
['authorized purpose allowed', state.authorizationAllowed],
|
|
311
|
+
['deletion duty still on time', state.dutyTimingOk],
|
|
312
|
+
['surveillance reuse prohibited', state.surveillanceReuseProhibited],
|
|
313
|
+
['package exists within budget', state.packageWithinBudget],
|
|
314
|
+
['package covers all active needs', state.packageCoversNeeds],
|
|
315
|
+
['lowest-cost eligible package chosen', state.cheapestEligibleChosen],
|
|
316
|
+
];
|
|
317
|
+
|
|
318
|
+
const lines = ['=== Check ==='];
|
|
319
|
+
for (const [name, ok] of checks) {
|
|
320
|
+
lines.push(`- ${ok ? 'PASS' : 'FAIL'}: ${name}`);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const failed = checks.filter(([, ok]) => !ok);
|
|
324
|
+
return {
|
|
325
|
+
text: lines.join('\n'),
|
|
326
|
+
ok: failed.length === 0,
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function main() {
|
|
331
|
+
const state = runDemo();
|
|
332
|
+
const answer = renderAnswer(state);
|
|
333
|
+
const reason = renderReasonWhy(state);
|
|
334
|
+
const check = renderCheck(state);
|
|
335
|
+
|
|
336
|
+
console.log(answer);
|
|
337
|
+
console.log('');
|
|
338
|
+
console.log(reason);
|
|
339
|
+
console.log('');
|
|
340
|
+
console.log(check.text);
|
|
341
|
+
|
|
342
|
+
if (!check.ok) {
|
|
343
|
+
process.exitCode = 1;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (require.main === module) {
|
|
348
|
+
main();
|
|
349
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
=== Answer ===
|
|
2
|
+
Name: Flandor
|
|
3
|
+
Region: Flanders
|
|
4
|
+
Metric: regional_retooling_priority
|
|
5
|
+
Active need count: 3/3
|
|
6
|
+
Recommended package: Flandor Retooling Pulse
|
|
7
|
+
Budget cap: €140M
|
|
8
|
+
Package cost: €120M
|
|
9
|
+
Payload SHA-256: 718f5b17d07ab6a95503bc04a1000ddb132409f600659c03d21def81914b780b
|
|
10
|
+
Envelope HMAC-SHA-256: 955968ca99a191783bc00cba068128ccb9ff40a5e6114fda13a52c74ee27329e
|
|
11
|
+
|
|
12
|
+
=== Reason Why ===
|
|
13
|
+
Export weakness is active because at least one cluster has exportOrdersIndex < 90 (Antwerp chemicals=84, Ghent manufacturing=87).
|
|
14
|
+
Skills strain is active because technical vacancy rate is 4.6% (threshold > 3.9%).
|
|
15
|
+
Grid stress is active because congestion hours = 19 (threshold > 11).
|
|
16
|
+
The recommendation policy is "lowest_cost_package_covering_all_active_needs", so the cheapest package that covers all active needs within budget is selected.
|
|
17
|
+
Selected package "Flandor Retooling Pulse" covers export=true, skills=true, grid=true, cost=€120M.
|
|
18
|
+
Usage is permitted only for purpose "regional_stabilization" and the envelope expires at 2026-04-08T19:00:00+00:00.
|
|
19
|
+
|
|
20
|
+
=== Check ===
|
|
21
|
+
- PASS: payload hash matches
|
|
22
|
+
- PASS: hmac matches
|
|
23
|
+
- PASS: threshold reached
|
|
24
|
+
- PASS: scope complete
|
|
25
|
+
- PASS: minimization respected
|
|
26
|
+
- PASS: authorized purpose allowed
|
|
27
|
+
- PASS: deletion duty still on time
|
|
28
|
+
- PASS: surveillance reuse prohibited
|
|
29
|
+
- PASS: package exists within budget
|
|
30
|
+
- PASS: package covers all active needs
|
|
31
|
+
- PASS: lowest-cost eligible package chosen
|