@nac3/forge-cli 1.9.3 → 1.12.0
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/dist/chat/doctrine_digest.d.ts.map +1 -1
- package/dist/chat/doctrine_digest.js +15 -0
- package/dist/chat/doctrine_digest.js.map +1 -1
- package/dist/chat/panel.d.ts.map +1 -1
- package/dist/chat/panel.js +13363 -12980
- package/dist/chat/panel.js.map +1 -1
- package/dist/chat/server.d.ts.map +1 -1
- package/dist/chat/server.js +76 -11
- package/dist/chat/server.js.map +1 -1
- package/dist/chat/sizing_section.d.ts +9 -0
- package/dist/chat/sizing_section.d.ts.map +1 -0
- package/dist/chat/sizing_section.js +73 -0
- package/dist/chat/sizing_section.js.map +1 -0
- package/dist/chat/tools/app_backend_dispatch.d.ts.map +1 -1
- package/dist/chat/tools/app_backend_dispatch.js +46 -0
- package/dist/chat/tools/app_backend_dispatch.js.map +1 -1
- package/dist/chat/tools/pending.d.ts +8 -0
- package/dist/chat/tools/pending.d.ts.map +1 -1
- package/dist/chat/tools/pending.js +26 -0
- package/dist/chat/tools/pending.js.map +1 -1
- package/dist/chat/tools/shell.d.ts +5 -0
- package/dist/chat/tools/shell.d.ts.map +1 -1
- package/dist/chat/tools/shell.js +60 -0
- package/dist/chat/tools/shell.js.map +1 -1
- package/dist/chat/tools/sizing.d.ts +66 -0
- package/dist/chat/tools/sizing.d.ts.map +1 -0
- package/dist/chat/tools/sizing.js +171 -0
- package/dist/chat/tools/sizing.js.map +1 -0
- package/dist/chat/tools.d.ts.map +1 -1
- package/dist/chat/tools.js +25 -2
- package/dist/chat/tools.js.map +1 -1
- package/dist/commands/clarify.d.ts.map +1 -1
- package/dist/commands/clarify.js +163 -1
- package/dist/commands/clarify.js.map +1 -1
- package/dist/commands/pending.d.ts.map +1 -1
- package/dist/commands/pending.js +9 -0
- package/dist/commands/pending.js.map +1 -1
- package/dist/commands/triage.d.ts +61 -7
- package/dist/commands/triage.d.ts.map +1 -1
- package/dist/commands/triage.js +106 -13
- package/dist/commands/triage.js.map +1 -1
- package/dist/core/forge_profile.d.ts +10 -0
- package/dist/core/forge_profile.d.ts.map +1 -1
- package/dist/core/forge_profile.js +6 -0
- package/dist/core/forge_profile.js.map +1 -1
- package/dist/docs/doctrine/gates.md +33 -21
- package/dist/docs/doctrine/sizing.md +182 -0
- package/dist/nac3/backend_tools.d.ts +14 -0
- package/dist/nac3/backend_tools.d.ts.map +1 -1
- package/dist/nac3/backend_tools.js +31 -1
- package/dist/nac3/backend_tools.js.map +1 -1
- package/dist/nac3/internal_manifest.d.ts.map +1 -1
- package/dist/nac3/internal_manifest.js +172 -5
- package/dist/nac3/internal_manifest.js.map +1 -1
- package/dist/pending/commit_files.d.ts +3 -0
- package/dist/pending/commit_files.d.ts.map +1 -0
- package/dist/pending/commit_files.js +42 -0
- package/dist/pending/commit_files.js.map +1 -0
- package/dist/pending/store.d.ts +12 -0
- package/dist/pending/store.d.ts.map +1 -1
- package/dist/pending/store.js +66 -0
- package/dist/pending/store.js.map +1 -1
- package/dist/pending/types.d.ts +50 -0
- package/dist/pending/types.d.ts.map +1 -1
- package/dist/pending/types.js.map +1 -1
- package/dist/relay/in_flight_turn.d.ts +56 -0
- package/dist/relay/in_flight_turn.d.ts.map +1 -0
- package/dist/relay/in_flight_turn.js +72 -0
- package/dist/relay/in_flight_turn.js.map +1 -0
- package/dist/relay/relay_publisher.d.ts +6 -0
- package/dist/relay/relay_publisher.d.ts.map +1 -1
- package/dist/relay/relay_publisher.js +17 -0
- package/dist/relay/relay_publisher.js.map +1 -1
- package/dist/sizing/model.d.ts +200 -0
- package/dist/sizing/model.d.ts.map +1 -0
- package/dist/sizing/model.js +423 -0
- package/dist/sizing/model.js.map +1 -0
- package/dist/sizing/store.d.ts +114 -0
- package/dist/sizing/store.d.ts.map +1 -0
- package/dist/sizing/store.js +235 -0
- package/dist/sizing/store.js.map +1 -0
- package/dist/tasks/supervisor.d.ts +21 -0
- package/dist/tasks/supervisor.d.ts.map +1 -1
- package/dist/tasks/supervisor.js +39 -3
- package/dist/tasks/supervisor.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/dist/workflow/definitions.js +1 -1
- package/dist/workflow/definitions.js.map +1 -1
- package/dist/workflow/state.d.ts +28 -0
- package/dist/workflow/state.d.ts.map +1 -1
- package/dist/workflow/state.js.map +1 -1
- package/docs/doctrine/gates.md +33 -21
- package/docs/doctrine/sizing.md +182 -0
- package/package.json +117 -117
- package/src/i18n/catalogs/ar.json +1 -0
- package/src/i18n/catalogs/de.json +1 -0
- package/src/i18n/catalogs/en.json +1 -0
- package/src/i18n/catalogs/es.json +1 -0
- package/src/i18n/catalogs/fr.json +1 -0
- package/src/i18n/catalogs/hi.json +1 -0
- package/src/i18n/catalogs/it.json +1 -0
- package/src/i18n/catalogs/ja.json +1 -0
- package/src/i18n/catalogs/pt.json +1 -0
- package/src/i18n/catalogs/zh.json +1 -0
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SIZING MODEL -- fixed architecture options with validity envelopes.
|
|
3
|
+
*
|
|
4
|
+
* Why this exists (Pablo, 2026-10-11, after the ADIP-KIKAN post mortem).
|
|
5
|
+
*
|
|
6
|
+
* docs/doctrine/concurrency-scale.md already said the right thing on its
|
|
7
|
+
* first line: "Every application Forge designs MUST be sized ... Scale is
|
|
8
|
+
* a RELEVAMIENTO input, not an afterthought bolted on when the app falls
|
|
9
|
+
* over." ADIP-KIKAN was built anyway with its whole working set resident
|
|
10
|
+
* in the Node heap. At its real volumetry (5.000 affiliates x 40 campaigns
|
|
11
|
+
* per year -> ~3,4M rows/year -> ~8 GB of live heap per year against a
|
|
12
|
+
* 2-4 GB V8 ceiling) that design hits a wall in months.
|
|
13
|
+
*
|
|
14
|
+
* So the lesson is NOT "write more doctrine". The doctrine was there and
|
|
15
|
+
* it was prose, and prose is advisory. Four concrete holes made the
|
|
16
|
+
* failure possible, and this module closes the first three:
|
|
17
|
+
*
|
|
18
|
+
* 1. The only volumetry Forge asked for was PEAK CONCURRENCY
|
|
19
|
+
* (src/commands/triage.ts), used solely to pick a complexity tier.
|
|
20
|
+
* Kikan has 100 concurrent users -- a low number -- and died of ROW
|
|
21
|
+
* COUNT. Data volume x retention was never a question.
|
|
22
|
+
* 2. There was no HARDWARE envelope at all: no RAM, no runtime heap
|
|
23
|
+
* ceiling, no disk. The architecture was chosen without knowing the
|
|
24
|
+
* machine, and the ceiling that kills the process (V8's
|
|
25
|
+
* heap_size_limit) is a property of the machine + runtime, not of the
|
|
26
|
+
* business.
|
|
27
|
+
* 3. The architecture choice was FREE-FORM. Forge invented an approach
|
|
28
|
+
* and wrote a rationale; there was no fixed option set with validity
|
|
29
|
+
* ranges, so there was nothing to violate.
|
|
30
|
+
* 4. The only gate touching volumetry was SOFT (gates.md GS4), and soft
|
|
31
|
+
* gates do not block. That one is closed by the hard gate in
|
|
32
|
+
* src/sizing/store.ts + the G9 entry in docs/doctrine/gates.md.
|
|
33
|
+
*
|
|
34
|
+
* The shape of the fix, consistent with the project's own rule that rules
|
|
35
|
+
* are ENFORCEMENT and not prompt: the numbers are DERIVED here from
|
|
36
|
+
* declared inputs, each architecture option declares the envelope in
|
|
37
|
+
* which it is admissible, and choosing outside the envelope is a REJECTED
|
|
38
|
+
* call -- not a judgement the brain can argue its way out of.
|
|
39
|
+
*
|
|
40
|
+
* This module is pure: no I/O, no process state. Persistence and the gate
|
|
41
|
+
* live in ./store.ts.
|
|
42
|
+
*
|
|
43
|
+
* ASCII-only.
|
|
44
|
+
*/
|
|
45
|
+
/** Index + row overhead on disk relative to the runtime row size. */
|
|
46
|
+
const DISK_OVERHEAD_FACTOR = 2.5;
|
|
47
|
+
const DAYS_PER_YEAR = 365;
|
|
48
|
+
export function derive(input) {
|
|
49
|
+
const years = Math.max(1, input.horizon_years || 1);
|
|
50
|
+
const rowsPerYear = input.entities.reduce((acc, e) => acc + (e.count || 0) * (e.events_per_year || 0) * (e.rows_per_event || 0), 0);
|
|
51
|
+
const rowsAtHorizon = rowsPerYear * years;
|
|
52
|
+
const traceShare = clamp01(input.trace_share);
|
|
53
|
+
const businessShare = 1 - traceShare;
|
|
54
|
+
/* Years each class survives. Retention 0 = forever = the whole horizon. */
|
|
55
|
+
const businessYears = input.retention.business_days > 0
|
|
56
|
+
? Math.min(years, input.retention.business_days / DAYS_PER_YEAR)
|
|
57
|
+
: years;
|
|
58
|
+
const traceYears = input.retention.trace_days > 0
|
|
59
|
+
? Math.min(years, input.retention.trace_days / DAYS_PER_YEAR)
|
|
60
|
+
: years;
|
|
61
|
+
const liveBusiness = rowsPerYear * businessShare * businessYears;
|
|
62
|
+
const liveTrace = rowsPerYear * traceShare * traceYears;
|
|
63
|
+
const liveRows = liveBusiness + liveTrace;
|
|
64
|
+
const heapMb = (liveRows * (input.bytes_per_row || 0)) / (1024 * 1024);
|
|
65
|
+
const heapCap = Math.max(1, input.hardware.heap_cap_mb || 1);
|
|
66
|
+
const diskGb = (liveRows * (input.bytes_per_row || 0) * DISK_OVERHEAD_FACTOR) / (1024 * 1024 * 1024);
|
|
67
|
+
const diskCap = Math.max(1, input.hardware.disk_gb || 1);
|
|
68
|
+
const unboundedClasses = [];
|
|
69
|
+
if (input.retention.business_days <= 0 && businessShare > 0)
|
|
70
|
+
unboundedClasses.push('business');
|
|
71
|
+
if (input.retention.trace_days <= 0 && traceShare > 0)
|
|
72
|
+
unboundedClasses.push('trace');
|
|
73
|
+
return {
|
|
74
|
+
rows_per_year: round(rowsPerYear),
|
|
75
|
+
rows_at_horizon: round(rowsAtHorizon),
|
|
76
|
+
live_rows: round(liveRows),
|
|
77
|
+
live_business_rows: round(liveBusiness),
|
|
78
|
+
live_trace_rows: round(liveTrace),
|
|
79
|
+
heap_mb_if_resident: round1(heapMb),
|
|
80
|
+
heap_pct_if_resident: round1((heapMb / heapCap) * 100),
|
|
81
|
+
disk_gb_at_horizon: round1(diskGb),
|
|
82
|
+
disk_pct_at_horizon: round1((diskGb / diskCap) * 100),
|
|
83
|
+
unbounded: unboundedClasses.length > 0,
|
|
84
|
+
unbounded_classes: unboundedClasses,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function clamp01(n) {
|
|
88
|
+
if (!Number.isFinite(n))
|
|
89
|
+
return 0;
|
|
90
|
+
return Math.min(1, Math.max(0, n));
|
|
91
|
+
}
|
|
92
|
+
function round(n) {
|
|
93
|
+
return Number.isFinite(n) ? Math.round(n) : 0;
|
|
94
|
+
}
|
|
95
|
+
function round1(n) {
|
|
96
|
+
return Number.isFinite(n) ? Math.round(n * 10) / 10 : 0;
|
|
97
|
+
}
|
|
98
|
+
/* ------------------------------------------------------------------ *
|
|
99
|
+
* The axes and their fixed options
|
|
100
|
+
* ------------------------------------------------------------------ */
|
|
101
|
+
export const AXES = ['state_residency', 'pagination', 'retention', 'write_concurrency'];
|
|
102
|
+
export const AXIS_LABEL = {
|
|
103
|
+
state_residency: 'Where the working set lives',
|
|
104
|
+
pagination: 'How a list page is produced',
|
|
105
|
+
retention: 'What bounds each data class',
|
|
106
|
+
write_concurrency: 'What makes a multi-step mutation atomic',
|
|
107
|
+
};
|
|
108
|
+
/**
|
|
109
|
+
* The share of the heap ceiling a resident working set may occupy.
|
|
110
|
+
*
|
|
111
|
+
* 25% and not 80%: the heap also holds every request in flight, the
|
|
112
|
+
* declarative catalog, the HTTP buffers and V8's own slack, and
|
|
113
|
+
* mark-compact pauses grow with the LIVE set long before the ceiling is
|
|
114
|
+
* reached -- the system gets unusable before it crashes. A quarter is the
|
|
115
|
+
* line where a resident set is a cache and not a liability.
|
|
116
|
+
*/
|
|
117
|
+
export const RESIDENT_HEAP_PCT_MAX = 25;
|
|
118
|
+
/** Above this many rows an in-memory page slice stops being honest. */
|
|
119
|
+
export const IN_MEMORY_PAGE_ROWS_MAX = 100_000;
|
|
120
|
+
/** Above this share of the disk, "keep everything forever" is a plan to run out. */
|
|
121
|
+
export const RETENTION_NONE_DISK_PCT_MAX = 25;
|
|
122
|
+
const ok = (reason) => ({ ok: true, reason });
|
|
123
|
+
const no = (reason) => ({ ok: false, reason });
|
|
124
|
+
export const OPTIONS = [
|
|
125
|
+
/* ---------------- state_residency ---------------- */
|
|
126
|
+
{
|
|
127
|
+
id: 'memory-only',
|
|
128
|
+
axis: 'state_residency',
|
|
129
|
+
label: 'In memory, no durability',
|
|
130
|
+
what: 'A process-local map. Restarting loses everything.',
|
|
131
|
+
envelope: ({ input }) => input.peak_concurrency > 0
|
|
132
|
+
? no('only valid with zero users (dev / test / a prototype nobody depends on); this sizing declares '
|
|
133
|
+
+ input.peak_concurrency + ' peak concurrent users')
|
|
134
|
+
: ok('no users declared: a prototype may keep its state in memory'),
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
id: 'memory-write-through',
|
|
138
|
+
axis: 'state_residency',
|
|
139
|
+
label: 'Resident working set + write-through to the database',
|
|
140
|
+
what: 'Reads are synchronous off an in-process copy; every write also goes '
|
|
141
|
+
+ 'to the database. Boot rehydrates the whole set.',
|
|
142
|
+
envelope: ({ derived }) => {
|
|
143
|
+
if (derived.unbounded) {
|
|
144
|
+
return no('inadmissible while any data class is kept forever ('
|
|
145
|
+
+ derived.unbounded_classes.join(', ') + '): a resident set with no bound has no ceiling. '
|
|
146
|
+
+ 'Declare retention per class first, or choose sql-async');
|
|
147
|
+
}
|
|
148
|
+
if (derived.heap_pct_if_resident >= RESIDENT_HEAP_PCT_MAX) {
|
|
149
|
+
return no('the live set would be ' + derived.heap_mb_if_resident + ' MB = '
|
|
150
|
+
+ derived.heap_pct_if_resident + '% of the ' + 'declared heap ceiling (max allowed: '
|
|
151
|
+
+ RESIDENT_HEAP_PCT_MAX + '%)');
|
|
152
|
+
}
|
|
153
|
+
return ok('bounded by retention and ' + derived.heap_pct_if_resident
|
|
154
|
+
+ '% of the heap ceiling, under the ' + RESIDENT_HEAP_PCT_MAX + '% limit');
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
id: 'sql-async',
|
|
159
|
+
axis: 'state_residency',
|
|
160
|
+
label: 'Database only, asynchronous reads',
|
|
161
|
+
what: 'No in-process copy. Every read is an indexed query; the database '
|
|
162
|
+
+ 'buffer pool is the cache. The default.',
|
|
163
|
+
envelope: () => ok('always admissible: growth lands on disk, not on the heap'),
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
id: 'sql-plus-narrow-cache',
|
|
167
|
+
axis: 'state_residency',
|
|
168
|
+
label: 'Database only, plus ONE narrow cache',
|
|
169
|
+
what: 'sql-async plus a cache for a single measured hot read, with '
|
|
170
|
+
+ 'explicit invalidation.',
|
|
171
|
+
envelope: ({ input }) => (input.measured_hotspot || '').trim().length > 0
|
|
172
|
+
? ok('justified by a measurement: ' + (input.measured_hotspot || '').trim())
|
|
173
|
+
: no('inadmissible without a measurement naming the hot read '
|
|
174
|
+
+ '(set measured_hotspot); a cache added on a hunch buys a second source of truth for nothing'),
|
|
175
|
+
},
|
|
176
|
+
/* ---------------- pagination ---------------- */
|
|
177
|
+
{
|
|
178
|
+
id: 'in-memory-slice',
|
|
179
|
+
axis: 'pagination',
|
|
180
|
+
label: 'Slice an already materialised collection',
|
|
181
|
+
what: 'Load the collection, filter, sort, then slice the page.',
|
|
182
|
+
envelope: ({ derived }) => {
|
|
183
|
+
if (derived.unbounded) {
|
|
184
|
+
return no('inadmissible while a data class is unbounded: the slice protects the wire, not the server');
|
|
185
|
+
}
|
|
186
|
+
if (derived.rows_at_horizon > IN_MEMORY_PAGE_ROWS_MAX) {
|
|
187
|
+
return no(derived.rows_at_horizon + ' rows at the horizon exceeds the '
|
|
188
|
+
+ IN_MEMORY_PAGE_ROWS_MAX + '-row limit for materialising before slicing');
|
|
189
|
+
}
|
|
190
|
+
return ok('the collection stays under ' + IN_MEMORY_PAGE_ROWS_MAX + ' rows');
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
id: 'sql-limit-offset',
|
|
195
|
+
axis: 'pagination',
|
|
196
|
+
label: 'LIMIT / OFFSET with COUNT(*)',
|
|
197
|
+
what: 'The database produces the page and the total.',
|
|
198
|
+
envelope: () => ok('always admissible'),
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
id: 'sql-keyset',
|
|
202
|
+
axis: 'pagination',
|
|
203
|
+
label: 'Keyset (seek) pagination',
|
|
204
|
+
what: 'Page by the last key seen. Constant cost at any depth.',
|
|
205
|
+
envelope: () => ok('always admissible, and the right default past a few hundred thousand rows'),
|
|
206
|
+
},
|
|
207
|
+
/* ---------------- retention ---------------- */
|
|
208
|
+
{
|
|
209
|
+
id: 'retention-none',
|
|
210
|
+
axis: 'retention',
|
|
211
|
+
label: 'Keep everything, forever',
|
|
212
|
+
what: 'Nothing is ever deleted.',
|
|
213
|
+
envelope: ({ derived, choices }) => {
|
|
214
|
+
const residency = choices.state_residency;
|
|
215
|
+
if (residency === 'memory-only' || residency === 'memory-write-through') {
|
|
216
|
+
return no('inadmissible together with a resident working set: forever + in memory is a wall with a date on it');
|
|
217
|
+
}
|
|
218
|
+
if (derived.disk_pct_at_horizon > RETENTION_NONE_DISK_PCT_MAX) {
|
|
219
|
+
return no('would reach ' + derived.disk_gb_at_horizon + ' GB = '
|
|
220
|
+
+ derived.disk_pct_at_horizon + '% of the declared disk by the horizon (max allowed: '
|
|
221
|
+
+ RETENTION_NONE_DISK_PCT_MAX + '%)');
|
|
222
|
+
}
|
|
223
|
+
return ok('fits the disk with room: ' + derived.disk_pct_at_horizon + '% at the horizon');
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
id: 'retention-by-age',
|
|
228
|
+
axis: 'retention',
|
|
229
|
+
label: 'Delete by age, per data class',
|
|
230
|
+
what: 'Each class declares how long it is kept; a scheduled job deletes '
|
|
231
|
+
+ 'past it, IN BULK (one statement per table, never row by row).',
|
|
232
|
+
envelope: ({ input }) => (input.retention.business_days > 0 || input.retention.trace_days > 0)
|
|
233
|
+
? ok('at least one class declares a retention window')
|
|
234
|
+
: no('both classes are declared as kept forever, so this option decides nothing: '
|
|
235
|
+
+ 'set business_days and/or trace_days'),
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
id: 'retention-by-age-and-count',
|
|
239
|
+
axis: 'retention',
|
|
240
|
+
label: 'Delete by age AND by row count',
|
|
241
|
+
what: 'Age window plus a hard cap per table, whichever hits first.',
|
|
242
|
+
/* Same precondition as retention-by-age, and for the same reason: this
|
|
243
|
+
* is a SUPERSET of it, so without a declared window there is no age to
|
|
244
|
+
* apply and the option would decide nothing while still passing the
|
|
245
|
+
* gate. Found by tests/sizing-gate.test.ts. Consequence, deliberate:
|
|
246
|
+
* with retention 0/0 the retention axis has NO admissible option, so a
|
|
247
|
+
* project cannot be sized at all until it declares what bounds each
|
|
248
|
+
* data class -- which is the ADIP-KIKAN lesson in one line. */
|
|
249
|
+
envelope: ({ input }) => (input.retention.business_days > 0 || input.retention.trace_days > 0)
|
|
250
|
+
? ok('a window is declared, and the row cap is what survives a traffic spike')
|
|
251
|
+
: no('both classes are declared as kept forever: declare business_days and/or '
|
|
252
|
+
+ 'trace_days first -- with nothing bounded there is no age to delete by'),
|
|
253
|
+
},
|
|
254
|
+
/* ---------------- write_concurrency ---------------- */
|
|
255
|
+
{
|
|
256
|
+
id: 'implicit-single-thread',
|
|
257
|
+
axis: 'write_concurrency',
|
|
258
|
+
label: 'Implicit: the runtime is single threaded',
|
|
259
|
+
what: 'No transactions. Atomicity comes from nothing being able to '
|
|
260
|
+
+ 'interleave between two synchronous statements.',
|
|
261
|
+
envelope: ({ input, choices }) => {
|
|
262
|
+
const residency = choices.state_residency;
|
|
263
|
+
const memoryResident = residency === 'memory-only' || residency === 'memory-write-through';
|
|
264
|
+
if (!memoryResident) {
|
|
265
|
+
return no('inadmissible once reads are asynchronous: an await between a check and its write '
|
|
266
|
+
+ 'lets a second request through, and every check-then-act becomes a race');
|
|
267
|
+
}
|
|
268
|
+
if (input.peak_concurrency > 1) {
|
|
269
|
+
return no('inadmissible with ' + input.peak_concurrency + ' concurrent users: the guarantee is '
|
|
270
|
+
+ 'implicit, undocumented, and nobody can see it in the code');
|
|
271
|
+
}
|
|
272
|
+
return ok('single user and a synchronous resident store: the guarantee holds, but write it down');
|
|
273
|
+
},
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
id: 'explicit-transactions',
|
|
277
|
+
axis: 'write_concurrency',
|
|
278
|
+
label: 'Explicit transactions',
|
|
279
|
+
what: 'Every multi-step mutation runs inside BEGIN / COMMIT. No '
|
|
280
|
+
+ 'transaction is held across an external call.',
|
|
281
|
+
envelope: () => ok('always admissible'),
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
id: 'unique-keys-and-upsert',
|
|
285
|
+
axis: 'write_concurrency',
|
|
286
|
+
label: 'Natural UNIQUE keys plus upsert',
|
|
287
|
+
what: 'Idempotency enforced by the database (UNIQUE on the natural key + '
|
|
288
|
+
+ 'INSERT ... ON DUPLICATE KEY) instead of check-then-act.',
|
|
289
|
+
envelope: () => ok('always admissible, and the only correct fix for a check-then-act race'),
|
|
290
|
+
},
|
|
291
|
+
];
|
|
292
|
+
export function optionsForAxis(axis) {
|
|
293
|
+
return OPTIONS.filter((o) => o.axis === axis);
|
|
294
|
+
}
|
|
295
|
+
export function findOption(id) {
|
|
296
|
+
return OPTIONS.find((o) => o.id === id);
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Reject an input that decides nothing. A record of zeros would otherwise
|
|
300
|
+
* pass every envelope and the gate would become a formality -- which is
|
|
301
|
+
* exactly the failure mode being fixed, one level up.
|
|
302
|
+
*/
|
|
303
|
+
export function validateInput(input) {
|
|
304
|
+
const errs = [];
|
|
305
|
+
if (!Array.isArray(input.entities) || input.entities.length === 0) {
|
|
306
|
+
errs.push('entities: declare at least one business entity with its count and event rate');
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
input.entities.forEach((e, i) => {
|
|
310
|
+
const at = 'entities[' + i + ']';
|
|
311
|
+
if (!e.name || !e.name.trim())
|
|
312
|
+
errs.push(at + '.name: required');
|
|
313
|
+
if (!(e.count > 0))
|
|
314
|
+
errs.push(at + '.count must be > 0');
|
|
315
|
+
if (!(e.events_per_year > 0))
|
|
316
|
+
errs.push(at + '.events_per_year must be > 0');
|
|
317
|
+
if (!(e.rows_per_event > 0)) {
|
|
318
|
+
errs.push(at + '.rows_per_event must be > 0 -- count EVERY table an event writes, '
|
|
319
|
+
+ 'not just the main one (in ADIP-KIKAN one event leaves ~17 rows)');
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
if (!(input.bytes_per_row > 0))
|
|
324
|
+
errs.push('bytes_per_row must be > 0 (retained bytes per row in the runtime)');
|
|
325
|
+
if (!(input.horizon_years > 0))
|
|
326
|
+
errs.push('horizon_years must be > 0');
|
|
327
|
+
if (!Number.isFinite(input.peak_concurrency) || input.peak_concurrency < 0) {
|
|
328
|
+
errs.push('peak_concurrency must be a number >= 0');
|
|
329
|
+
}
|
|
330
|
+
if (!input.retention || !Number.isFinite(input.retention.business_days) || !Number.isFinite(input.retention.trace_days)) {
|
|
331
|
+
errs.push('retention.business_days and retention.trace_days are required (0 means kept forever)');
|
|
332
|
+
}
|
|
333
|
+
const hw = input.hardware;
|
|
334
|
+
if (!hw) {
|
|
335
|
+
errs.push('hardware is required: ram_mb, heap_cap_mb, disk_gb, vcpus');
|
|
336
|
+
}
|
|
337
|
+
else {
|
|
338
|
+
if (!(hw.ram_mb > 0))
|
|
339
|
+
errs.push('hardware.ram_mb must be > 0');
|
|
340
|
+
if (!(hw.heap_cap_mb > 0)) {
|
|
341
|
+
errs.push('hardware.heap_cap_mb must be > 0 -- the RUNTIME heap ceiling '
|
|
342
|
+
+ '(Node: node -e "console.log(require(\'v8\').getHeapStatistics().heap_size_limit)"), not the RAM of the machine');
|
|
343
|
+
}
|
|
344
|
+
if (hw.heap_cap_mb > hw.ram_mb) {
|
|
345
|
+
errs.push('hardware.heap_cap_mb (' + hw.heap_cap_mb + ') cannot exceed ram_mb (' + hw.ram_mb + ')');
|
|
346
|
+
}
|
|
347
|
+
if (!(hw.disk_gb > 0))
|
|
348
|
+
errs.push('hardware.disk_gb must be > 0');
|
|
349
|
+
if (!(hw.vcpus > 0))
|
|
350
|
+
errs.push('hardware.vcpus must be > 0');
|
|
351
|
+
}
|
|
352
|
+
if (input.trace_share !== undefined && (input.trace_share < 0 || input.trace_share > 1)) {
|
|
353
|
+
errs.push('trace_share must be between 0 and 1');
|
|
354
|
+
}
|
|
355
|
+
return errs;
|
|
356
|
+
}
|
|
357
|
+
/** The choice set this sizing admits, preferring the safest option. */
|
|
358
|
+
export function recommend(input, derived) {
|
|
359
|
+
const base = {};
|
|
360
|
+
/* Residency first: other axes depend on it. */
|
|
361
|
+
base.state_residency = 'sql-async';
|
|
362
|
+
base.write_concurrency = 'explicit-transactions';
|
|
363
|
+
base.retention = derived.unbounded || derived.disk_pct_at_horizon > RETENTION_NONE_DISK_PCT_MAX
|
|
364
|
+
? 'retention-by-age'
|
|
365
|
+
: 'retention-none';
|
|
366
|
+
base.pagination = derived.rows_at_horizon > IN_MEMORY_PAGE_ROWS_MAX || derived.unbounded
|
|
367
|
+
? (derived.rows_at_horizon > 1_000_000 ? 'sql-keyset' : 'sql-limit-offset')
|
|
368
|
+
: 'sql-limit-offset';
|
|
369
|
+
void input;
|
|
370
|
+
return base;
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Validate a full choice set. Fail-closed on every axis: an axis with no
|
|
374
|
+
* choice, an unknown option, or an option outside its envelope all fail.
|
|
375
|
+
*/
|
|
376
|
+
export function validateChoices(input, choices) {
|
|
377
|
+
const inputErrors = validateInput(input);
|
|
378
|
+
const derived = derive(input);
|
|
379
|
+
const ctx = { input, derived, choices };
|
|
380
|
+
const axes = AXES.map((axis) => {
|
|
381
|
+
const admissible = optionsForAxis(axis)
|
|
382
|
+
.filter((o) => o.envelope(ctx).ok)
|
|
383
|
+
.map((o) => o.id);
|
|
384
|
+
const chosen = choices[axis];
|
|
385
|
+
if (!chosen) {
|
|
386
|
+
return { axis, ok: false, reason: 'axis not decided; admissible: ' + (admissible.join(', ') || 'none'), admissible };
|
|
387
|
+
}
|
|
388
|
+
const opt = findOption(chosen);
|
|
389
|
+
if (!opt || opt.axis !== axis) {
|
|
390
|
+
return {
|
|
391
|
+
axis, chosen, ok: false,
|
|
392
|
+
reason: 'unknown option "' + chosen + '" for this axis; valid ids: '
|
|
393
|
+
+ optionsForAxis(axis).map((o) => o.id).join(', '),
|
|
394
|
+
admissible,
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
const v = opt.envelope(ctx);
|
|
398
|
+
return { axis, chosen, ok: v.ok, reason: v.reason, admissible };
|
|
399
|
+
});
|
|
400
|
+
return {
|
|
401
|
+
ok: inputErrors.length === 0 && axes.every((a) => a.ok),
|
|
402
|
+
derived,
|
|
403
|
+
axes,
|
|
404
|
+
input_errors: inputErrors,
|
|
405
|
+
recommended: recommend(input, derived),
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
/* ------------------------------------------------------------------ *
|
|
409
|
+
* Rendering (shared by the verb result, the gate and the prompt)
|
|
410
|
+
* ------------------------------------------------------------------ */
|
|
411
|
+
export function renderDerived(d) {
|
|
412
|
+
return [
|
|
413
|
+
'rows/year: ' + d.rows_per_year.toLocaleString('en-US'),
|
|
414
|
+
'rows at horizon: ' + d.rows_at_horizon.toLocaleString('en-US'),
|
|
415
|
+
'live rows after retention: ' + d.live_rows.toLocaleString('en-US')
|
|
416
|
+
+ ' (business ' + d.live_business_rows.toLocaleString('en-US')
|
|
417
|
+
+ ', trace ' + d.live_trace_rows.toLocaleString('en-US') + ')',
|
|
418
|
+
'resident heap if in memory: ' + d.heap_mb_if_resident + ' MB = ' + d.heap_pct_if_resident + '% of the ceiling',
|
|
419
|
+
'disk at horizon: ' + d.disk_gb_at_horizon + ' GB = ' + d.disk_pct_at_horizon + '% of the disk',
|
|
420
|
+
'unbounded classes: ' + (d.unbounded ? d.unbounded_classes.join(', ') : 'none'),
|
|
421
|
+
];
|
|
422
|
+
}
|
|
423
|
+
//# sourceMappingURL=model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/sizing/model.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { type Axis, type SizingInput, type SizingVerdict } from './model.js';
|
|
2
|
+
export interface SizingRevision {
|
|
3
|
+
at: string;
|
|
4
|
+
/** Who recorded it: a session id, 'owner', or the verb caller. */
|
|
5
|
+
by: string;
|
|
6
|
+
/** One line on what changed and why. Mandatory: a silent re-sizing
|
|
7
|
+
* is how a wrong number survives a year. */
|
|
8
|
+
note: string;
|
|
9
|
+
input: SizingInput;
|
|
10
|
+
choices: Record<Axis, string>;
|
|
11
|
+
}
|
|
12
|
+
export interface SizingRecord {
|
|
13
|
+
version: 1;
|
|
14
|
+
project_slug: string;
|
|
15
|
+
updated_at: string;
|
|
16
|
+
input: SizingInput;
|
|
17
|
+
choices: Record<Axis, string>;
|
|
18
|
+
/** Free text: the owner's reasoning, kept for the next reader. */
|
|
19
|
+
rationale?: string;
|
|
20
|
+
/**
|
|
21
|
+
* When a surface stopped being a prototype. Once set, the gate applies
|
|
22
|
+
* outside any workflow. ISO date.
|
|
23
|
+
*/
|
|
24
|
+
load_bearing_since?: string;
|
|
25
|
+
/** Every previous recording, newest last. Never pruned: the history IS
|
|
26
|
+
* the audit trail of how the sizing drifted. */
|
|
27
|
+
revisions: SizingRevision[];
|
|
28
|
+
}
|
|
29
|
+
export declare function readSizing(project_slug: string): Promise<SizingRecord | undefined>;
|
|
30
|
+
export declare function writeSizing(rec: SizingRecord): Promise<void>;
|
|
31
|
+
export interface RecordSizingInput {
|
|
32
|
+
project_slug: string;
|
|
33
|
+
input: SizingInput;
|
|
34
|
+
choices: Partial<Record<Axis, string>>;
|
|
35
|
+
note: string;
|
|
36
|
+
by?: string;
|
|
37
|
+
rationale?: string;
|
|
38
|
+
load_bearing_since?: string;
|
|
39
|
+
}
|
|
40
|
+
export interface RecordSizingResult {
|
|
41
|
+
ok: boolean;
|
|
42
|
+
verdict: SizingVerdict;
|
|
43
|
+
/** Set only when ok: the record as persisted. */
|
|
44
|
+
record?: SizingRecord;
|
|
45
|
+
/** Set when !ok: nothing was written. */
|
|
46
|
+
refused?: string;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Record a sizing. REFUSES to persist a choice set that is outside its
|
|
50
|
+
* envelope, so the file on disk can only ever hold an admissible sizing.
|
|
51
|
+
*
|
|
52
|
+
* That is deliberate and it is the whole mechanism: if an inadmissible
|
|
53
|
+
* sizing could be stored with a warning, the gate downstream would have
|
|
54
|
+
* to decide whether to believe it, and "decide whether to believe it" is
|
|
55
|
+
* exactly the judgement call the brain talks its way out of.
|
|
56
|
+
*/
|
|
57
|
+
export declare function recordSizing(inp: RecordSizingInput): Promise<RecordSizingResult>;
|
|
58
|
+
export type SizingGateState = 'ok' | 'missing' | 'stale' | 'not_required';
|
|
59
|
+
export interface SizingGateResult {
|
|
60
|
+
state: SizingGateState;
|
|
61
|
+
ok: boolean;
|
|
62
|
+
/** One line for the refusal message / the prompt. */
|
|
63
|
+
reason: string;
|
|
64
|
+
/** Present when a record exists. */
|
|
65
|
+
verdict?: SizingVerdict;
|
|
66
|
+
record?: SizingRecord;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Evaluate the gate for a project.
|
|
70
|
+
*
|
|
71
|
+
* `construction` is true when the caller is about to enter (or is in) the
|
|
72
|
+
* construction stage of a workflow. Outside construction the gate still
|
|
73
|
+
* applies once `load_bearing_since` is set -- that is the prototype
|
|
74
|
+
* trigger from the header.
|
|
75
|
+
*/
|
|
76
|
+
export declare function sizingGate(project_slug: string, opts?: {
|
|
77
|
+
construction?: boolean;
|
|
78
|
+
}): Promise<SizingGateResult>;
|
|
79
|
+
export interface ObservedVolume {
|
|
80
|
+
/** Rows actually counted, per table or in total. */
|
|
81
|
+
rows_total: number;
|
|
82
|
+
/** Bytes actually retained per row, if measured. */
|
|
83
|
+
bytes_per_row?: number;
|
|
84
|
+
/** Over how many days those rows accumulated. */
|
|
85
|
+
over_days: number;
|
|
86
|
+
/** Where the numbers came from. Required: an unsourced number is a guess. */
|
|
87
|
+
source: string;
|
|
88
|
+
}
|
|
89
|
+
export interface DriftResult {
|
|
90
|
+
ok: boolean;
|
|
91
|
+
/** Declared rows/year vs the rate the observation implies. */
|
|
92
|
+
declared_rows_per_year: number;
|
|
93
|
+
observed_rows_per_year: number;
|
|
94
|
+
ratio: number;
|
|
95
|
+
/** Beyond this the sizing has to be redone. */
|
|
96
|
+
threshold: number;
|
|
97
|
+
verdict: string;
|
|
98
|
+
/** The gate verdict recomputed with the OBSERVED rate substituted in. */
|
|
99
|
+
with_observed?: SizingVerdict;
|
|
100
|
+
}
|
|
101
|
+
/** Past this ratio between observed and declared, the sizing is wrong
|
|
102
|
+
* enough that the architecture choice has to be re-validated. 2x and not
|
|
103
|
+
* 10x because the point is to catch it in month 2, not month 12. */
|
|
104
|
+
export declare const DRIFT_THRESHOLD = 2;
|
|
105
|
+
/**
|
|
106
|
+
* Compare a real measurement against the declared sizing, and -- this is
|
|
107
|
+
* the useful part -- re-run the envelopes with the OBSERVED rate
|
|
108
|
+
* substituted in. The answer is not "you were 3x off", it is "at the rate
|
|
109
|
+
* you actually have, the architecture you chose is no longer admissible".
|
|
110
|
+
*/
|
|
111
|
+
export declare function checkDrift(project_slug: string, observed: ObservedVolume): Promise<DriftResult & {
|
|
112
|
+
record?: SizingRecord;
|
|
113
|
+
}>;
|
|
114
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/sizing/store.ts"],"names":[],"mappings":"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"}
|