@uwmd/core 1.7.0 → 1.8.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/agents/bancroft.js +4 -1
- package/dist/agents/bancroft.js.map +1 -1
- package/dist/asset-class.d.ts +98 -0
- package/dist/asset-class.d.ts.map +1 -0
- package/dist/asset-class.js +226 -0
- package/dist/asset-class.js.map +1 -0
- package/dist/browser.d.ts +21 -7
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +22 -4
- package/dist/browser.js.map +1 -1
- package/dist/calc/errors.d.ts +1 -1
- package/dist/calc/errors.d.ts.map +1 -1
- package/dist/calc/errors.js.map +1 -1
- package/dist/calc/evaluator.js +25 -1
- package/dist/calc/evaluator.js.map +1 -1
- package/dist/calc/prng.d.ts +59 -0
- package/dist/calc/prng.d.ts.map +1 -0
- package/dist/calc/prng.js +181 -0
- package/dist/calc/prng.js.map +1 -0
- package/dist/calc/sensitivity.d.ts +76 -0
- package/dist/calc/sensitivity.d.ts.map +1 -0
- package/dist/calc/sensitivity.js +160 -0
- package/dist/calc/sensitivity.js.map +1 -0
- package/dist/calc/stochastic.d.ts +97 -0
- package/dist/calc/stochastic.d.ts.map +1 -0
- package/dist/calc/stochastic.js +265 -0
- package/dist/calc/stochastic.js.map +1 -0
- package/dist/cascade.d.ts.map +1 -1
- package/dist/cascade.js +8 -4
- package/dist/cascade.js.map +1 -1
- package/dist/cli-args.d.ts +37 -0
- package/dist/cli-args.d.ts.map +1 -0
- package/dist/cli-args.js +93 -0
- package/dist/cli-args.js.map +1 -0
- package/dist/cli.js +119 -72
- package/dist/cli.js.map +1 -1
- package/dist/editor.js +25 -6
- package/dist/editor.js.map +1 -1
- package/dist/index.d.ts +22 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -4
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +16 -4
- package/dist/init.js.map +1 -1
- package/dist/integrity-canonical.d.ts.map +1 -1
- package/dist/integrity-canonical.js +10 -2
- package/dist/integrity-canonical.js.map +1 -1
- package/dist/integrity.d.ts +92 -4
- package/dist/integrity.d.ts.map +1 -1
- package/dist/integrity.js +195 -9
- package/dist/integrity.js.map +1 -1
- package/dist/migrate-source-tags.d.ts +34 -0
- package/dist/migrate-source-tags.d.ts.map +1 -0
- package/dist/migrate-source-tags.js +179 -0
- package/dist/migrate-source-tags.js.map +1 -0
- package/dist/module-runtime.d.ts +57 -0
- package/dist/module-runtime.d.ts.map +1 -0
- package/dist/module-runtime.js +167 -0
- package/dist/module-runtime.js.map +1 -0
- package/dist/module-signing.d.ts +85 -0
- package/dist/module-signing.d.ts.map +1 -0
- package/dist/module-signing.js +136 -0
- package/dist/module-signing.js.map +1 -0
- package/dist/modules.d.ts +42 -0
- package/dist/modules.d.ts.map +1 -1
- package/dist/modules.js +124 -1
- package/dist/modules.js.map +1 -1
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +27 -1
- package/dist/parser.js.map +1 -1
- package/dist/protocol.d.ts +191 -21
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +289 -32
- package/dist/protocol.js.map +1 -1
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +9 -3
- package/dist/runner.js.map +1 -1
- package/dist/types.d.ts +118 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +44 -0
- package/dist/types.js.map +1 -1
- package/dist/validator.d.ts +26 -2
- package/dist/validator.d.ts.map +1 -1
- package/dist/validator.js +201 -19
- package/dist/validator.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +6 -2
package/dist/types.d.ts
CHANGED
|
@@ -8,7 +8,12 @@ export type DealStage = 'scope' | 'screening' | 'term_sheet' | 'full_underwrite'
|
|
|
8
8
|
* 'agent/L6-01', 'document/rent_roll'). Free-form strings remain valid for
|
|
9
9
|
* forward compat; this union enumerates the canonical values.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
11
|
+
* **RFC 0031 split the field.** `_meta.source` is now actor-only
|
|
12
|
+
* (`manual | agent/<id> | document/<id> | system/<id> | institution/<id>`);
|
|
13
|
+
* the resolution-method tags below belong in `_meta.resolution`. A canonical
|
|
14
|
+
* tag found in `_meta.source` is interpreted at read time as `resolution`
|
|
15
|
+
* (with the actor treated as absent) and warns under `SRC-02`. See protocol
|
|
16
|
+
* §V.3/§V.7 and format spec §2.6.
|
|
12
17
|
*/
|
|
13
18
|
export type SourceTag = 'user_input' | 'user_override' | 'manual'
|
|
14
19
|
/**
|
|
@@ -26,7 +31,48 @@ export type SourceTag = 'user_input' | 'user_override' | 'manual'
|
|
|
26
31
|
* recognize this tag MUST NOT rewrite it to `user_input`.
|
|
27
32
|
*/
|
|
28
33
|
| 'market_data_accepted' | 'ai_extracted' | 'agent_computed' | 'asset_class_default' | 'scenario_default' | 'global_default' | 'system_default' | (string & {});
|
|
34
|
+
/**
|
|
35
|
+
* The full set of canonical short-form source tags. As of RFC 0031 these are
|
|
36
|
+
* the **resolution-method** vocabulary — producers stamp them into
|
|
37
|
+
* `_meta.resolution` (a tag found in `_meta.source` is legacy and is
|
|
38
|
+
* interpreted as `resolution` at read time). Eight of these match
|
|
39
|
+
* `CascadeStep` 1:1; the rest are non-cascade tags — `manual`,
|
|
40
|
+
* `ai_extracted`, `agent_computed`, `scenario_default`, and
|
|
41
|
+
* `market_data_accepted`.
|
|
42
|
+
*
|
|
43
|
+
* `market_data_accepted` (RFC 0022 §4) is deliberately *not* a cascade step:
|
|
44
|
+
* it is an in-file value of record that resolves at the `user_input` step
|
|
45
|
+
* while keeping its own tag, because a value accepted for lack of better
|
|
46
|
+
* evidence must stay distinguishable from one someone typed in.
|
|
47
|
+
*
|
|
48
|
+
* `scenario_default` is retained but its meaning is sharpened to mean
|
|
49
|
+
* "value derived from a named scenario in the file or institution
|
|
50
|
+
* config." Producers needing a generic fallback SHOULD use
|
|
51
|
+
* `system_default` instead.
|
|
52
|
+
*
|
|
53
|
+
* Actor patterns (`manual`, `agent/L6-01`, `document/rent_roll`, …) are a
|
|
54
|
+
* separate vocabulary — see `ACTOR_NAMESPACES` / `parseActorSource` in
|
|
55
|
+
* `protocol.ts`.
|
|
56
|
+
*/
|
|
57
|
+
export declare const SOURCE_TAGS: readonly ["user_input", "user_override", "manual", "inherited_assumption", "investor_profile", "market_data", "market_data_accepted", "ai_extracted", "agent_computed", "asset_class_default", "scenario_default", "global_default", "system_default"];
|
|
58
|
+
export type CanonicalSourceTag = (typeof SOURCE_TAGS)[number];
|
|
29
59
|
export type AssetClass = 'multifamily' | 'office' | 'retail' | 'industrial' | 'self_storage' | 'hospitality' | 'mixed_use' | 'senior_housing' | 'student_housing' | 'land';
|
|
60
|
+
/**
|
|
61
|
+
* An asset class as it may appear in a document: a builtin, or a
|
|
62
|
+
* module-declared custom identifier (RFC 0003, protocol §X.2).
|
|
63
|
+
*
|
|
64
|
+
* Deliberately separate from {@link AssetClass}, which stays a closed union.
|
|
65
|
+
* Folding custom ids into `AssetClass` would collapse it to `string` and take
|
|
66
|
+
* `ASSET_CLASS_MEMBERS`, every pack and layout lookup's narrowing, and the
|
|
67
|
+
* §XIII / §5.1 class tables down with it. Anything that needs a *builtin*
|
|
68
|
+
* still asks for `AssetClass`; this type is for the boundary — frontmatter and
|
|
69
|
+
* module declarations — where a custom class is legal.
|
|
70
|
+
*
|
|
71
|
+
* The `(string & {})` arm keeps editor autocomplete on the builtin members
|
|
72
|
+
* while accepting any identifier; `parseAssetClass` is what actually validates
|
|
73
|
+
* one.
|
|
74
|
+
*/
|
|
75
|
+
export type UWAssetClassId = AssetClass | (string & {});
|
|
30
76
|
/**
|
|
31
77
|
* Every v1 asset class, as a runtime list. Derived from {@link AssetClass} via
|
|
32
78
|
* {@link ASSET_CLASS_MEMBERS}, so it is exactly the union — no more, no less.
|
|
@@ -40,7 +86,16 @@ export interface UWFieldOverride {
|
|
|
40
86
|
/** Dot-notated path relative to the block's content root (e.g. "units[7].current_rent"). */
|
|
41
87
|
path: string;
|
|
42
88
|
confidence?: ConfidenceLevel;
|
|
89
|
+
/** Actor override for this field (RFC 0031 actor grammar; legacy tags are
|
|
90
|
+
* read as `resolution` — see §2.6 read-time interpretation). */
|
|
43
91
|
source?: SourceTag;
|
|
92
|
+
/**
|
|
93
|
+
* How this field's value was resolved (a canonical `SOURCE_TAGS` member,
|
|
94
|
+
* e.g. the cascade step that produced it — protocol §V.7 permits stamping
|
|
95
|
+
* at leaf level). A leaf-level `resolution` wins over the block-level one
|
|
96
|
+
* for its path. RFC 0031.
|
|
97
|
+
*/
|
|
98
|
+
resolution?: SourceTag;
|
|
44
99
|
reason?: 'illegible' | 'missing' | 'overridden' | 'estimated' | 'computed';
|
|
45
100
|
note?: string;
|
|
46
101
|
}
|
|
@@ -48,7 +103,20 @@ export interface UWMeta {
|
|
|
48
103
|
section: string;
|
|
49
104
|
version: number;
|
|
50
105
|
superseded: boolean;
|
|
106
|
+
/**
|
|
107
|
+
* Who wrote the block — `manual` or `<namespace>/<id>` with a registered
|
|
108
|
+
* actor namespace (RFC 0031; grammar in format spec §2.6, authority in
|
|
109
|
+
* protocol §V.3). Legacy canonical tags in this field are interpreted as
|
|
110
|
+
* `resolution` at read time and warn under `SRC-02`.
|
|
111
|
+
*/
|
|
51
112
|
source: SourceTag;
|
|
113
|
+
/**
|
|
114
|
+
* How the block's value was resolved — one canonical `SOURCE_TAGS` member
|
|
115
|
+
* (`user_input`, `asset_class_default`, …). Optional and orthogonal to
|
|
116
|
+
* `source`: an agent that filled a field from the asset-class default table
|
|
117
|
+
* has both an actor and a method. RFC 0031; protocol §V.7.
|
|
118
|
+
*/
|
|
119
|
+
resolution?: SourceTag;
|
|
52
120
|
agent_id: string | null;
|
|
53
121
|
agent_version: string | null;
|
|
54
122
|
actor: string;
|
|
@@ -87,10 +155,20 @@ export interface UWMeta {
|
|
|
87
155
|
* in that chain MUST.
|
|
88
156
|
*/
|
|
89
157
|
parent_hash?: string | null;
|
|
158
|
+
/**
|
|
159
|
+
* Detached signature over this block's signing input (protocol §V.11,
|
|
160
|
+
* RFC 0010). Optional and opaque to core: the wire shape is normative here,
|
|
161
|
+
* but the cryptography lives in `@uwmd/signing` so that the library stays
|
|
162
|
+
* dependency-free for the overwhelming majority of adopters who never sign.
|
|
163
|
+
*
|
|
164
|
+
* Excluded from `content_hash` canonicalization along with `content_hash`
|
|
165
|
+
* itself, so stamping a signature does not invalidate the hash it covers.
|
|
166
|
+
*/
|
|
167
|
+
signature?: UWBlockSignature;
|
|
90
168
|
/**
|
|
91
169
|
* Which observation set a `market_data_accepted` value was promoted from
|
|
92
|
-
* (RFC 0022 §4). REQUIRED whenever `
|
|
93
|
-
* meaningless otherwise.
|
|
170
|
+
* (RFC 0022 §4). REQUIRED whenever `resolution` (or, pre-split, `source`)
|
|
171
|
+
* is `market_data_accepted`, and meaningless otherwise.
|
|
94
172
|
*
|
|
95
173
|
* Without it, "accepted from market data" is an unfalsifiable claim: a
|
|
96
174
|
* reviewer could see the tag but never recover *which* observations, of which
|
|
@@ -99,12 +177,42 @@ export interface UWMeta {
|
|
|
99
177
|
market_data_ref?: MarketDataRef;
|
|
100
178
|
/**
|
|
101
179
|
* Which ancestor asserted an `inherited_assumption` value (RFC 0021 §5).
|
|
102
|
-
* REQUIRED whenever `
|
|
180
|
+
* REQUIRED whenever `resolution` (or, pre-split, `source`) is
|
|
181
|
+
* `inherited_assumption`, and meaningless
|
|
103
182
|
* otherwise — an inherited value with no named ancestor is indistinguishable
|
|
104
183
|
* from an ambient default, which is exactly what §5 forbids.
|
|
105
184
|
*/
|
|
106
185
|
inherited_from?: InheritedFrom;
|
|
107
186
|
}
|
|
187
|
+
/**
|
|
188
|
+
* A detached signature over a block's signing input (protocol §V.11).
|
|
189
|
+
*
|
|
190
|
+
* What it covers is deliberately narrow — `content_hash`, `section`, `actor`,
|
|
191
|
+
* `timestamp`, `kid`, `signed_at` — and deliberately excludes `parent_hash`.
|
|
192
|
+
* Signing is per-block authorship; chain integrity is the separate
|
|
193
|
+
* `content_hash`/`parent_hash` mechanism. Coupling them would make re-rooting a
|
|
194
|
+
* chain after a merge invalidate every prior signature, which is a legitimate
|
|
195
|
+
* edit destroying evidence it has no business touching.
|
|
196
|
+
*/
|
|
197
|
+
export interface UWBlockSignature {
|
|
198
|
+
/** Algorithm identifier. Closed set at protocol 1.x; see §V.11. */
|
|
199
|
+
alg: UWSignatureAlgorithm;
|
|
200
|
+
/** Opaque key identifier the verifier looks up in its own key store. */
|
|
201
|
+
kid: string;
|
|
202
|
+
/** Base64url-encoded (unpadded) signature bytes. */
|
|
203
|
+
sig: string;
|
|
204
|
+
/**
|
|
205
|
+
* ISO 8601 instant the signature was produced. MAY differ from
|
|
206
|
+
* `_meta.timestamp` — a block edited offline is signed when re-uploaded.
|
|
207
|
+
*/
|
|
208
|
+
signed_at: string;
|
|
209
|
+
/** Signing-protocol version. Absent means `"1"`. */
|
|
210
|
+
v?: string;
|
|
211
|
+
}
|
|
212
|
+
/** The algorithms protocol 1.x admits for `_meta.signature.alg`. */
|
|
213
|
+
export type UWSignatureAlgorithm = 'ed25519' | 'es256' | 'es384';
|
|
214
|
+
/** Runtime mirror of {@link UWSignatureAlgorithm}, for validation. */
|
|
215
|
+
export declare const UW_SIGNATURE_ALGORITHMS: readonly UWSignatureAlgorithm[];
|
|
108
216
|
/** Identity and digest of the ancestor that asserted an inherited value. */
|
|
109
217
|
export interface InheritedFrom {
|
|
110
218
|
document_id: string;
|
|
@@ -176,7 +284,12 @@ export interface UWFrontmatter {
|
|
|
176
284
|
city: string;
|
|
177
285
|
state: string;
|
|
178
286
|
zip: string;
|
|
179
|
-
|
|
287
|
+
/**
|
|
288
|
+
* Builtin, or a module-declared custom identifier (RFC 0003). A namespaced
|
|
289
|
+
* value obliges the file to name its modules in `modules` — see
|
|
290
|
+
* `declaredModuleDependencies`.
|
|
291
|
+
*/
|
|
292
|
+
asset_class: UWAssetClassId;
|
|
180
293
|
asset_subtype?: string | null;
|
|
181
294
|
loan_type?: string | null;
|
|
182
295
|
scenario?: string | null;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;AAExD,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,aAAa,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE3F,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AAE9D,MAAM,MAAM,SAAS,GACjB,OAAO,GACP,WAAW,GACX,YAAY,GACZ,iBAAiB,GACjB,iBAAiB,GACjB,SAAS,GACT,YAAY,CAAC;AAEjB
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
// .uw.md format — TypeScript type definitions
|
|
2
2
|
// Spec: UW_FORMAT_SPEC_v1.md v1.1
|
|
3
|
+
/**
|
|
4
|
+
* The full set of canonical short-form source tags. As of RFC 0031 these are
|
|
5
|
+
* the **resolution-method** vocabulary — producers stamp them into
|
|
6
|
+
* `_meta.resolution` (a tag found in `_meta.source` is legacy and is
|
|
7
|
+
* interpreted as `resolution` at read time). Eight of these match
|
|
8
|
+
* `CascadeStep` 1:1; the rest are non-cascade tags — `manual`,
|
|
9
|
+
* `ai_extracted`, `agent_computed`, `scenario_default`, and
|
|
10
|
+
* `market_data_accepted`.
|
|
11
|
+
*
|
|
12
|
+
* `market_data_accepted` (RFC 0022 §4) is deliberately *not* a cascade step:
|
|
13
|
+
* it is an in-file value of record that resolves at the `user_input` step
|
|
14
|
+
* while keeping its own tag, because a value accepted for lack of better
|
|
15
|
+
* evidence must stay distinguishable from one someone typed in.
|
|
16
|
+
*
|
|
17
|
+
* `scenario_default` is retained but its meaning is sharpened to mean
|
|
18
|
+
* "value derived from a named scenario in the file or institution
|
|
19
|
+
* config." Producers needing a generic fallback SHOULD use
|
|
20
|
+
* `system_default` instead.
|
|
21
|
+
*
|
|
22
|
+
* Actor patterns (`manual`, `agent/L6-01`, `document/rent_roll`, …) are a
|
|
23
|
+
* separate vocabulary — see `ACTOR_NAMESPACES` / `parseActorSource` in
|
|
24
|
+
* `protocol.ts`.
|
|
25
|
+
*/
|
|
26
|
+
export const SOURCE_TAGS = Object.freeze([
|
|
27
|
+
'user_input',
|
|
28
|
+
'user_override',
|
|
29
|
+
'manual',
|
|
30
|
+
'inherited_assumption',
|
|
31
|
+
'investor_profile',
|
|
32
|
+
'market_data',
|
|
33
|
+
'market_data_accepted',
|
|
34
|
+
'ai_extracted',
|
|
35
|
+
'agent_computed',
|
|
36
|
+
'asset_class_default',
|
|
37
|
+
'scenario_default',
|
|
38
|
+
'global_default',
|
|
39
|
+
'system_default',
|
|
40
|
+
]);
|
|
3
41
|
/**
|
|
4
42
|
* Exhaustiveness anchor for {@link AssetClass}.
|
|
5
43
|
*
|
|
@@ -34,6 +72,12 @@ const ASSET_CLASS_MEMBERS = {
|
|
|
34
72
|
* `spec/UW_FORMAT_SPEC_v1.md` and `spec/schemas/`, not an independent registry.
|
|
35
73
|
*/
|
|
36
74
|
export const ASSET_CLASSES = Object.freeze(Object.keys(ASSET_CLASS_MEMBERS));
|
|
75
|
+
/** Runtime mirror of {@link UWSignatureAlgorithm}, for validation. */
|
|
76
|
+
export const UW_SIGNATURE_ALGORITHMS = Object.freeze([
|
|
77
|
+
'ed25519',
|
|
78
|
+
'es256',
|
|
79
|
+
'es384',
|
|
80
|
+
]);
|
|
37
81
|
export const DEFAULT_THRESHOLDS = {
|
|
38
82
|
dscr: { error_below: 1.0, warning_below: 1.2 },
|
|
39
83
|
ltv: { warning_above: 0.75, error_above: 0.85 },
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,kCAAkC;
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,kCAAkC;AA2DlC;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC;IACvC,YAAY;IACZ,eAAe;IACf,QAAQ;IACR,sBAAsB;IACtB,kBAAkB;IAClB,aAAa;IACb,sBAAsB;IACtB,cAAc;IACd,gBAAgB;IAChB,qBAAqB;IACrB,kBAAkB;IAClB,gBAAgB;IAChB,gBAAgB;CACR,CAAC,CAAC;AAiCZ;;;;;;;;;;;;GAYG;AACH,MAAM,mBAAmB,GAA6B;IACpD,WAAW,EAAE,IAAI;IACjB,MAAM,EAAE,IAAI;IACZ,MAAM,EAAE,IAAI;IACZ,UAAU,EAAE,IAAI;IAChB,YAAY,EAAE,IAAI;IAClB,WAAW,EAAE,IAAI;IACjB,SAAS,EAAE,IAAI;IACf,cAAc,EAAE,IAAI;IACpB,eAAe,EAAE,IAAI;IACrB,IAAI,EAAE,IAAI;CACX,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,aAAa,GAA0B,MAAM,CAAC,MAAM,CAC/D,MAAM,CAAC,IAAI,CAAC,mBAAmB,CAAiB,CACjD,CAAC;AAmJF,sEAAsE;AACtE,MAAM,CAAC,MAAM,uBAAuB,GAAoC,MAAM,CAAC,MAAM,CAAC;IACpF,SAAS;IACT,OAAO;IACP,OAAO;CACR,CAAC,CAAC;AAyLH,MAAM,CAAC,MAAM,kBAAkB,GAAwB;IACrD,IAAI,EAAe,EAAE,WAAW,EAAE,GAAG,EAAG,aAAa,EAAE,GAAG,EAAE;IAC5D,GAAG,EAAgB,EAAE,aAAa,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE;IAC7D,UAAU,EAAS,EAAE,aAAa,EAAE,IAAI,EAAE;IAC1C,QAAQ,EAAW,EAAE,aAAa,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE;IAC/D,YAAY,EAAO,EAAE,aAAa,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE;IAC/D,UAAU,EAAS,EAAE,aAAa,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE;IAC/D,GAAG,EAAgB,EAAE,aAAa,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE;IAC/D,eAAe,EAAI,EAAE,aAAa,EAAE,GAAG,EAAG,aAAa,EAAE,GAAG,EAAE;IAC9D,kBAAkB,EAAC,EAAE,aAAa,EAAE,IAAI,EAAE;IAC1C,GAAG,EAAgB,EAAE,aAAa,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE;CAC9D,CAAC"}
|
package/dist/validator.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ParsedUWFile, ValidationMessage, ValidationResult, FinancialThresholds, DealStage } from './types.js';
|
|
1
|
+
import type { ParsedUWFile, ValidationMessage, ValidationResult, FinancialThresholds, AssetClass, DealStage } from './types.js';
|
|
2
2
|
import type { IssueRemediation, IncompleteDataPolicy } from './protocol.js';
|
|
3
3
|
export declare function lookupRemediation(code: string): IssueRemediation | undefined;
|
|
4
4
|
/**
|
|
@@ -20,9 +20,33 @@ export declare const STAGE_REQUIREMENTS: Record<DealStage, StageRequirement>;
|
|
|
20
20
|
* Legacy helper that returns just the section-list portion of a stage's
|
|
21
21
|
* requirements. Preserved for callers that pre-date the widened
|
|
22
22
|
* `StageRequirement` shape; new code SHOULD consult `STAGE_REQUIREMENTS`
|
|
23
|
-
* directly.
|
|
23
|
+
* directly (or `requiredSectionsFor` when the asset class is known).
|
|
24
24
|
*/
|
|
25
25
|
export declare function getRequiredSections(stage: DealStage): readonly string[];
|
|
26
|
+
/**
|
|
27
|
+
* Per-class adjustments to the base stage lists (format spec §5.1 class
|
|
28
|
+
* overlays, RFC 0029). Exhaustive by design: only `land` and `mixed_use`
|
|
29
|
+
* diverge structurally — every other class reuses the base sections with
|
|
30
|
+
* class-appropriate payloads (hospitality's `rent_roll` carries
|
|
31
|
+
* keys/ADR/segmentation), so reuse needs no entry here.
|
|
32
|
+
*
|
|
33
|
+
* `exempt` removes a requirement outright; `substitute` replaces it with
|
|
34
|
+
* another section that is then *required* in its place.
|
|
35
|
+
*/
|
|
36
|
+
export declare const STAGE_SECTION_OVERLAYS: Partial<Record<AssetClass, {
|
|
37
|
+
exempt?: readonly string[];
|
|
38
|
+
substitute?: Readonly<Record<string, string>>;
|
|
39
|
+
}>>;
|
|
40
|
+
/**
|
|
41
|
+
* The sections a declared stage requires of a document of the given asset
|
|
42
|
+
* class: the base list with the class overlay applied. A substitute that
|
|
43
|
+
* replaces more than one section appears once. An unrecognized class — or
|
|
44
|
+
* none — takes the base list verbatim. Both consumers of stage completeness
|
|
45
|
+
* (`stage_readiness` and `DQ-06`) resolve through this one function, so the
|
|
46
|
+
* booleans and the issues stream can never disagree about what a stage
|
|
47
|
+
* requires.
|
|
48
|
+
*/
|
|
49
|
+
export declare function requiredSectionsFor(stage: DealStage, assetClass?: string): readonly string[];
|
|
26
50
|
export declare function validateUWFile(parsed: ParsedUWFile, thresholdOverrides?: Partial<FinancialThresholds>): ValidationResult;
|
|
27
51
|
interface DataQualityOptions {
|
|
28
52
|
policies?: readonly IncompleteDataPolicy[];
|
package/dist/validator.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../src/validator.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,YAAY,EACZ,iBAAiB,EACjB,gBAAgB,EAChB,mBAAmB,EAEnB,SAAS,EACV,MAAM,YAAY,CAAC;AAMpB,OAAO,KAAK,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../src/validator.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,YAAY,EACZ,iBAAiB,EACjB,gBAAgB,EAChB,mBAAmB,EAEnB,UAAU,EACV,SAAS,EACV,MAAM,YAAY,CAAC;AAMpB,OAAO,KAAK,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAiB5E,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAE5E;AA4CD;;;;;;;;GAQG;AACH,MAAM,WAAW,gBAAgB;IAC/B,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC,eAAe,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC;CAC9B;AAED,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,SAAS,EAAE,gBAAgB,CAelE,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,MAAM,EAAE,CAEvE;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,sBAAsB,EAAE,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE;IAC9D,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,UAAU,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAC/C,CAAC,CAGD,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,SAAS,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAW5F;AAqBD,wBAAgB,cAAc,CAC5B,MAAM,EAAE,YAAY,EACpB,kBAAkB,CAAC,EAAE,OAAO,CAAC,mBAAmB,CAAC,GAChD,gBAAgB,CAuClB;AA21BD,UAAU,kBAAkB;IAC1B,QAAQ,CAAC,EAAE,SAAS,oBAAoB,EAAE,CAAC;IAC3C,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,8CAA8C;IAC9C,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,YAAY,EACpB,MAAM,EAAE,iBAAiB,EAAE,EAC3B,IAAI,GAAE,kBAAuB,GAC5B,IAAI,CAmGN"}
|
package/dist/validator.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
// .uw.md validator — financial validity checks (§5.2) + cross-section consistency (§5.3)
|
|
2
2
|
// Spec: UW_FORMAT_SPEC_v1.md Part V
|
|
3
|
-
import { DEFAULT_THRESHOLDS } from './types.js';
|
|
3
|
+
import { DEFAULT_THRESHOLDS, SOURCE_TAGS } from './types.js';
|
|
4
4
|
import { getSection, getSectionVariant, deepGet } from './parser.js';
|
|
5
|
-
import { BUILTIN_REMEDIATIONS, BUILTIN_INCOMPLETE_DATA_POLICIES, lookupIncompleteDataPolicy, getSizeIntensive, DEAL_UNDERWRITING_PROFILE } from './protocol.js';
|
|
5
|
+
import { BUILTIN_REMEDIATIONS, BUILTIN_INCOMPLETE_DATA_POLICIES, lookupIncompleteDataPolicy, getSizeIntensive, DEAL_UNDERWRITING_PROFILE, parseActorSource } from './protocol.js';
|
|
6
6
|
import { EXTERNAL_ANNOTATION_KEY } from './composition.js';
|
|
7
7
|
import { UW_LITE_SOURCE_EXTENSION } from './lite-bridge.js';
|
|
8
8
|
import { readGapsContent } from './gaps.js';
|
|
9
|
+
import { parseAssetClass, declaredModuleDependencies } from './asset-class.js';
|
|
9
10
|
// ─── BUILTIN_REMEDIATIONS lookup (UW_PROTOCOL_v1.md §III.6) ──────────────────
|
|
10
11
|
//
|
|
11
12
|
// Validator constructs inline messages with deal-specific values, but the
|
|
@@ -66,20 +67,61 @@ export const STAGE_REQUIREMENTS = {
|
|
|
66
67
|
},
|
|
67
68
|
screening: { required_sections: ['property', 'debt_structure', 'validation'] },
|
|
68
69
|
term_sheet: { required_sections: ['property', 'debt_structure', 'validation', 'rent_roll', 'borrower_sponsor', 'preliminary_sizing'] },
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
// `operating_statement` re-joined full_underwrite and above with RFC 0028
|
|
71
|
+
// (spec §5.1 always listed it; the variant-aware `hasSection` case below
|
|
72
|
+
// was built for it but no stage list reached it — decision (a) in the RFC).
|
|
73
|
+
full_underwrite: { required_sections: ['property', 'debt_structure', 'validation', 'rent_roll', 'borrower_sponsor', 'preliminary_sizing', 'operating_statement', 'noi_model', 'valuation', 'sources_uses', 'market_analysis'] },
|
|
74
|
+
credit_approval: { required_sections: ['property', 'debt_structure', 'validation', 'rent_roll', 'borrower_sponsor', 'preliminary_sizing', 'operating_statement', 'noi_model', 'valuation', 'sources_uses', 'market_analysis', 'dcf', 'stress_tests', 'risk_assessment', 'compliance', 'assumptions'] },
|
|
75
|
+
closing: { required_sections: ['property', 'debt_structure', 'validation', 'rent_roll', 'borrower_sponsor', 'preliminary_sizing', 'operating_statement', 'noi_model', 'valuation', 'sources_uses', 'market_analysis', 'dcf', 'stress_tests', 'risk_assessment', 'compliance', 'assumptions', 'due_diligence'] },
|
|
76
|
+
monitoring: { required_sections: ['property', 'debt_structure', 'validation', 'rent_roll', 'borrower_sponsor', 'preliminary_sizing', 'operating_statement', 'noi_model', 'valuation', 'sources_uses', 'market_analysis', 'dcf', 'stress_tests', 'risk_assessment', 'compliance', 'assumptions', 'due_diligence'] },
|
|
73
77
|
};
|
|
74
78
|
/**
|
|
75
79
|
* Legacy helper that returns just the section-list portion of a stage's
|
|
76
80
|
* requirements. Preserved for callers that pre-date the widened
|
|
77
81
|
* `StageRequirement` shape; new code SHOULD consult `STAGE_REQUIREMENTS`
|
|
78
|
-
* directly.
|
|
82
|
+
* directly (or `requiredSectionsFor` when the asset class is known).
|
|
79
83
|
*/
|
|
80
84
|
export function getRequiredSections(stage) {
|
|
81
85
|
return STAGE_REQUIREMENTS[stage].required_sections;
|
|
82
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* Per-class adjustments to the base stage lists (format spec §5.1 class
|
|
89
|
+
* overlays, RFC 0029). Exhaustive by design: only `land` and `mixed_use`
|
|
90
|
+
* diverge structurally — every other class reuses the base sections with
|
|
91
|
+
* class-appropriate payloads (hospitality's `rent_roll` carries
|
|
92
|
+
* keys/ADR/segmentation), so reuse needs no entry here.
|
|
93
|
+
*
|
|
94
|
+
* `exempt` removes a requirement outright; `substitute` replaces it with
|
|
95
|
+
* another section that is then *required* in its place.
|
|
96
|
+
*/
|
|
97
|
+
export const STAGE_SECTION_OVERLAYS = {
|
|
98
|
+
land: { exempt: ['rent_roll', 'operating_statement'] },
|
|
99
|
+
mixed_use: { substitute: { rent_roll: 'components', operating_statement: 'components' } },
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* The sections a declared stage requires of a document of the given asset
|
|
103
|
+
* class: the base list with the class overlay applied. A substitute that
|
|
104
|
+
* replaces more than one section appears once. An unrecognized class — or
|
|
105
|
+
* none — takes the base list verbatim. Both consumers of stage completeness
|
|
106
|
+
* (`stage_readiness` and `DQ-06`) resolve through this one function, so the
|
|
107
|
+
* booleans and the issues stream can never disagree about what a stage
|
|
108
|
+
* requires.
|
|
109
|
+
*/
|
|
110
|
+
export function requiredSectionsFor(stage, assetClass) {
|
|
111
|
+
const base = STAGE_REQUIREMENTS[stage].required_sections;
|
|
112
|
+
const overlay = assetClass ? STAGE_SECTION_OVERLAYS[assetClass] : undefined;
|
|
113
|
+
if (!overlay)
|
|
114
|
+
return base;
|
|
115
|
+
const out = [];
|
|
116
|
+
for (const section of base) {
|
|
117
|
+
if (overlay.exempt?.includes(section))
|
|
118
|
+
continue;
|
|
119
|
+
const replacement = overlay.substitute?.[section] ?? section;
|
|
120
|
+
if (!out.includes(replacement))
|
|
121
|
+
out.push(replacement);
|
|
122
|
+
}
|
|
123
|
+
return out;
|
|
124
|
+
}
|
|
83
125
|
/**
|
|
84
126
|
* Resolve a dot-notated path of the form `<section>.<field>...` against
|
|
85
127
|
* the parsed file's content blocks. Returns `undefined` when any segment
|
|
@@ -107,7 +149,10 @@ export function validateUWFile(parsed, thresholdOverrides) {
|
|
|
107
149
|
checkComponents(parsed, issues);
|
|
108
150
|
checkCapitalStack(parsed, issues);
|
|
109
151
|
checkSizeIntensive(parsed, issues);
|
|
152
|
+
checkSectionReadiness(parsed, issues);
|
|
153
|
+
checkAssetClassIdentifier(parsed, issues);
|
|
110
154
|
checkMetaIntegrity(parsed, issues);
|
|
155
|
+
checkSourceVocabulary(parsed, issues);
|
|
111
156
|
checkScopeReadiness(parsed, issues);
|
|
112
157
|
checkDataQuality(parsed, issues);
|
|
113
158
|
// Enrich every issue with BUILTIN_REMEDIATIONS title/remediation/spec_ref
|
|
@@ -638,6 +683,53 @@ function checkSizeIntensive(parsed, issues) {
|
|
|
638
683
|
value: value ?? null,
|
|
639
684
|
});
|
|
640
685
|
}
|
|
686
|
+
// ─── §5.3 CC-14 / §III.6a DQ-06 — section-level readiness (RFC 0028) ─────────
|
|
687
|
+
// CC-14 warns when a deal-record UWX document has no property section at all
|
|
688
|
+
// — §4.1 requires it at every stage, and before this rule the only trace of
|
|
689
|
+
// the gap was a stage_readiness boolean nothing downstream reads. Always a
|
|
690
|
+
// warning, never an error: the RFC 0028 Appendix A scan found 28 corpus
|
|
691
|
+
// documents a refusal would invalidate retroactively, and an institution
|
|
692
|
+
// wanting a hard gate has INCOMPLETE_DATA_POLICIES.
|
|
693
|
+
//
|
|
694
|
+
// DQ-06 then names, at info severity, each section the declared deal_stage
|
|
695
|
+
// requires but the file lacks — the sectional sibling of the field-level
|
|
696
|
+
// DQ-04, and the issues-stream mirror of stage_readiness. Info because the
|
|
697
|
+
// same scan shows deal_stage declarations state where a deal is going, not
|
|
698
|
+
// what the file contains (all twelve worked examples fail their declared
|
|
699
|
+
// stage's list); info reports the gap without refusing or nagging.
|
|
700
|
+
function checkSectionReadiness(parsed, issues) {
|
|
701
|
+
// CC-14 preconditions mirror CC-13's 1 and 2 (RFC 0028 §1). Precondition 3
|
|
702
|
+
// (not externalized) is satisfied structurally: an externalized-but-
|
|
703
|
+
// unresolved section still parses as a block, so it is present here.
|
|
704
|
+
const isCompiledLite = !!(parsed.sections[UW_LITE_SOURCE_EXTENSION] || parsed.extensions?.[UW_LITE_SOURCE_EXTENSION]);
|
|
705
|
+
const profile = parsed.frontmatter['document_profile'];
|
|
706
|
+
const isDealRecord = profile == null || profile === DEAL_UNDERWRITING_PROFILE;
|
|
707
|
+
const hasProperty = hasStageSection(parsed, 'property');
|
|
708
|
+
let cc14Fired = false;
|
|
709
|
+
if (!isCompiledLite && isDealRecord && !hasProperty) {
|
|
710
|
+
cc14Fired = true;
|
|
711
|
+
issues.push({
|
|
712
|
+
code: 'CC-14', severity: 'warning', section: 'property',
|
|
713
|
+
message: 'CC-14: this deal record has no property section; §4.1 requires it at every stage',
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
// DQ-06: one issue per missing required section of the declared stage.
|
|
717
|
+
// No stage declared → no claim to check (same posture as DQ-04). The
|
|
718
|
+
// property entry is suppressed when CC-14 fired: one defect, one diagnostic.
|
|
719
|
+
const stage = parsed.frontmatter.deal_stage;
|
|
720
|
+
if (!stage || !(stage in STAGE_REQUIREMENTS))
|
|
721
|
+
return;
|
|
722
|
+
for (const sectionId of requiredSectionsFor(stage, parsed.frontmatter.asset_class)) {
|
|
723
|
+
if (sectionId === 'property' && cc14Fired)
|
|
724
|
+
continue;
|
|
725
|
+
if (hasStageSection(parsed, sectionId))
|
|
726
|
+
continue;
|
|
727
|
+
issues.push({
|
|
728
|
+
code: 'DQ-06', severity: 'info', section: sectionId,
|
|
729
|
+
message: `DQ-06: ${stage} requires ${sectionId}; section is missing.`,
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
}
|
|
641
733
|
function checkCapitalStack(parsed, issues) {
|
|
642
734
|
const cs = getSection(parsed, 'capital_stack');
|
|
643
735
|
if (cs)
|
|
@@ -654,6 +746,50 @@ function checkCapitalStack(parsed, issues) {
|
|
|
654
746
|
}
|
|
655
747
|
}
|
|
656
748
|
}
|
|
749
|
+
// ─── Asset-class identifier (RFC 0003) ───────────────────────────────────────
|
|
750
|
+
/**
|
|
751
|
+
* The identifier itself, independent of whether any module is loaded.
|
|
752
|
+
*
|
|
753
|
+
* Deliberately separate from *resolution*: whether a custom class can be read
|
|
754
|
+
* depends on the host's loaded modules, and a validator that conflated the two
|
|
755
|
+
* would report the same file as valid or invalid depending on who ran it.
|
|
756
|
+
* What is checked here is the part that is true everywhere — the syntax, and
|
|
757
|
+
* the obligation a namespaced class carries to name its modules.
|
|
758
|
+
*/
|
|
759
|
+
function checkAssetClassIdentifier(parsed, issues) {
|
|
760
|
+
const raw = parsed.frontmatter?.asset_class;
|
|
761
|
+
if (typeof raw !== 'string' || raw.length === 0)
|
|
762
|
+
return;
|
|
763
|
+
const identity = parseAssetClass(raw);
|
|
764
|
+
if (!identity.ok) {
|
|
765
|
+
issues.push({
|
|
766
|
+
code: identity.error.code,
|
|
767
|
+
severity: 'error',
|
|
768
|
+
field: 'asset_class',
|
|
769
|
+
message: identity.error.message,
|
|
770
|
+
value: raw,
|
|
771
|
+
...(identity.error.remediation ? { remediation: identity.error.remediation } : {}),
|
|
772
|
+
});
|
|
773
|
+
return;
|
|
774
|
+
}
|
|
775
|
+
if (identity.kind === 'builtin')
|
|
776
|
+
return;
|
|
777
|
+
// A namespaced class with no `modules` list is unreadable by anyone who does
|
|
778
|
+
// not already happen to hold the right module: the file states a dependency
|
|
779
|
+
// it never names. Reported as a warning rather than an error because the
|
|
780
|
+
// document is still well-formed and a host that does hold the module reads
|
|
781
|
+
// it correctly — the cost falls on everyone else.
|
|
782
|
+
if (declaredModuleDependencies(parsed.frontmatter).length === 0) {
|
|
783
|
+
issues.push({
|
|
784
|
+
code: 'MOD-DEPENDENCY-UNDECLARED',
|
|
785
|
+
severity: 'warning',
|
|
786
|
+
field: 'modules',
|
|
787
|
+
message: `asset_class '${raw}' is module-declared, but frontmatter names no 'modules' to load.`,
|
|
788
|
+
value: raw,
|
|
789
|
+
remediation: `Add a 'modules' list naming the module that declares '${raw}', so a reader without it can say what to load rather than only that something is missing.`,
|
|
790
|
+
});
|
|
791
|
+
}
|
|
792
|
+
}
|
|
657
793
|
// ─── Meta integrity checks ────────────────────────────────────────────────────
|
|
658
794
|
function checkMetaIntegrity(parsed, issues) {
|
|
659
795
|
const REQUIRED_META_FIELDS = ['section', 'version', 'source', 'timestamp', 'confidence'];
|
|
@@ -683,24 +819,70 @@ function checkMetaIntegrity(parsed, issues) {
|
|
|
683
819
|
}
|
|
684
820
|
}
|
|
685
821
|
}
|
|
822
|
+
// ─── SRC-01 / SRC-02: source vocabulary (RFC 0031) ───────────────────────────
|
|
823
|
+
//
|
|
824
|
+
// `_meta.source` is actor-only: `manual` or `<namespace>/<id>` with a
|
|
825
|
+
// registered namespace. SRC-02 fires when the field holds a canonical
|
|
826
|
+
// SOURCE_TAGS value — a resolution method in the actor field, the pre-split
|
|
827
|
+
// spelling, read-time-interpreted as `resolution` (warning through format
|
|
828
|
+
// 1.x, error at 2.0). SRC-01 fires on everything else outside the grammar
|
|
829
|
+
// (colon forms, bare words). Both are warnings: every such block still
|
|
830
|
+
// parses, and edits against it resolve the conservative catch-all policy.
|
|
831
|
+
const NON_MANUAL_SOURCE_TAGS = new Set(SOURCE_TAGS.filter((t) => t !== 'manual'));
|
|
832
|
+
function checkSourceVocabulary(parsed, issues) {
|
|
833
|
+
for (const [sectionId, entry] of Object.entries(parsed.sections)) {
|
|
834
|
+
const blocks = isVariantMap(entry)
|
|
835
|
+
? Object.values(entry)
|
|
836
|
+
: [entry];
|
|
837
|
+
for (const block of blocks) {
|
|
838
|
+
const src = block.meta?.source;
|
|
839
|
+
if (typeof src !== 'string' || src.length === 0)
|
|
840
|
+
continue; // absence is META/DQ territory
|
|
841
|
+
if (NON_MANUAL_SOURCE_TAGS.has(src)) {
|
|
842
|
+
issues.push({
|
|
843
|
+
code: 'SRC-02',
|
|
844
|
+
severity: 'warning',
|
|
845
|
+
section: sectionId,
|
|
846
|
+
message: `Section ${sectionId} _meta.source is '${src}', a resolution tag in the actor field. Move it to _meta.resolution; readers treat the actor as absent.`,
|
|
847
|
+
});
|
|
848
|
+
continue;
|
|
849
|
+
}
|
|
850
|
+
if (parseActorSource(src).kind === 'invalid') {
|
|
851
|
+
issues.push({
|
|
852
|
+
code: 'SRC-01',
|
|
853
|
+
severity: 'warning',
|
|
854
|
+
section: sectionId,
|
|
855
|
+
message: `Section ${sectionId} _meta.source '${src}' is not 'manual' or '<namespace>/<id>' with a registered actor namespace (${['agent', 'document', 'system', 'institution'].join(', ')}).`,
|
|
856
|
+
});
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
}
|
|
686
861
|
// ─── Stage readiness ──────────────────────────────────────────────────────────
|
|
862
|
+
/**
|
|
863
|
+
* Variant-aware section presence, as stage readiness defines it: a
|
|
864
|
+
* multi-variant `operating_statement` counts through its `t12` or `default`
|
|
865
|
+
* variant. Shared by `computeStageReadiness` and the RFC 0028 checks
|
|
866
|
+
* (`CC-14`, `DQ-06`) so "present" means one thing.
|
|
867
|
+
*/
|
|
868
|
+
function hasStageSection(parsed, id) {
|
|
869
|
+
if (id === 'operating_statement') {
|
|
870
|
+
return !!(parsed.sections[id] || getSectionVariant(parsed, id, 't12') || getSectionVariant(parsed, id, 'default'));
|
|
871
|
+
}
|
|
872
|
+
return !!parsed.sections[id];
|
|
873
|
+
}
|
|
687
874
|
function computeStageReadiness(parsed) {
|
|
688
|
-
const hasSection = (id) =>
|
|
689
|
-
if (id === 'operating_statement') {
|
|
690
|
-
return !!(parsed.sections[id] || getSectionVariant(parsed, id, 't12') || getSectionVariant(parsed, id, 'default'));
|
|
691
|
-
}
|
|
692
|
-
if (id === 'stress_tests' || id === 'due_diligence') {
|
|
693
|
-
return !!(parsed.sections[id]);
|
|
694
|
-
}
|
|
695
|
-
return !!parsed.sections[id];
|
|
696
|
-
};
|
|
875
|
+
const hasSection = (id) => hasStageSection(parsed, id);
|
|
697
876
|
const hasFieldPath = (path) => {
|
|
698
877
|
const v = resolveSectionFieldPath(parsed, path);
|
|
699
878
|
return v !== undefined && v !== null && v !== '';
|
|
700
879
|
};
|
|
701
880
|
const stageReady = (stage) => {
|
|
702
881
|
const req = STAGE_REQUIREMENTS[stage];
|
|
703
|
-
|
|
882
|
+
// Class overlays (RFC 0029): resolve through the same function DQ-06
|
|
883
|
+
// uses, so readiness and the issues stream cannot disagree.
|
|
884
|
+
const requiredSections = requiredSectionsFor(stage, parsed.frontmatter.asset_class);
|
|
885
|
+
if (!requiredSections.every(s => hasSection(s)))
|
|
704
886
|
return false;
|
|
705
887
|
if (req.required_field_paths && !req.required_field_paths.every(hasFieldPath))
|
|
706
888
|
return false;
|
|
@@ -828,8 +1010,8 @@ export function checkDataQuality(parsed, issues, opts = {}) {
|
|
|
828
1010
|
// accepted but never recover which observations, of which vintage. That
|
|
829
1011
|
// is precisely the gap the profile exists to close, so a block asserting
|
|
830
1012
|
// the tag without the reference is worse than one tagged plainly.
|
|
831
|
-
const
|
|
832
|
-
|
|
1013
|
+
const promotedTag = (o) => o.resolution === 'market_data_accepted' || o.source === 'market_data_accepted';
|
|
1014
|
+
const promoted = promotedTag(m) || m.field_overrides?.some(promotedTag);
|
|
833
1015
|
if (promoted && !m.market_data_ref) {
|
|
834
1016
|
issues.push({
|
|
835
1017
|
code: 'DQ-06',
|