Stage S3 — IPAL
Provenance. Every F below carries the date of the run that established it; the runs are listed in the comment above. Where two runs disagree, the later one is named as such and both are kept.
Purpose
Abacus can now price the product; IPAL does not know it exists. This stage creates the IPAL product carrying the same configuration — the catalogue an operator will actually see and sell: product, objects, covers with their definitions (limits/deductibles), risks, events, documents, annex types, and the pricing factors the quotation screen asks. It runs the same sub-stage loop as every stage: take the previous stage's outputs, generate the plan, confirm with the user, apply, test (and reverse if necessary), modify in conversation with the human until they are satisfied, produce deployment scripts, deploy, test again.
Inputs → Outputs
In: the S2 configuration summary (§ The S2 → S3 handover below) + the whitelabel specification (names, definitions, documents, annexes, business wishes). Out: a quotable, issuable product on the working environment; the stage's script set; the catalogue facts the Offer and Serdica stages build on.
The S2 → S3 handover
What is passed
P S2 hands over one artefact — the configuration summary, a validated document with a published schema, not prose. It carries, each field with its source and each defined in Stage Abacus § SUM-1…6:
| What crosses | Why S3 needs it |
|---|---|
| Covers as rated — cover code × object code, rate dimension, the factors in the base expression, the grid axes as authored, calculation order, currency, underwriting caps | PR_BASIC_COVERS is one row per product × cover × object; a set difference means a cover that prices nothing, or a rated cover nobody can select |
| Pricing factors the rating keys on — code, the grid-axis name, datatype, required-ness at quote stage, the exact value codes | the set S3 must guarantee exists, with the codes the compiled grid actually looks up |
| Loadings, discounts and taxes — code as written, type and sign (a discount is a loading with sign −1), scope (product, or cover × object), rating file or flat amount, driver factors, order, rate dimension and base, currency, combination rule, caps | scope is the highest-value field: a policy-level loading with no matching product-scope rating row is a hard failure, and the combination rule and order change the premium |
| Version and template identity — product id, versions with their stage, effective date, template code, loading and tax rules | the engine reads only DEPLOYED versions, and the template code is the single offer↔rating bridge |
| Required features nothing holds — the values injected by a backend operation rather than answered by an operator | those are a PR_OPERATIONS row, not a factor, and are not derivable from the specification at all |
| Worked examples — the exact payload and the expected figures per cover | S3's acceptance oracle: the quote must render the same figure S2 reconciled |
| What the rating could not hold, and what is still open | each with a real destination (PR_OPERATIONS, the offer tables, a filter → gate Hd) and, where a rate is provisional, the cap on the proof level S3 may claim |
P Names, cover definitions, risks, events, documents, annex types, objects and offers do not cross — S3 reads those from the specification. A smaller correct contract beats an invented one.
How it is applied — by skill, never by hand
P ⚠ S3 does not retype the summary. The abacus seed skill projects it into IPAL catalogue rows — cover codes with their object, the loading list with type and sign, the factor skeleton — written as proposals that S3 then adopts, completes from the specification, or rejects with a reason (D75, D74; Product Configurator § C-3). Everything it cannot derive is authored through ipal configure, which is likewise a skill over the system's own configuration endpoints and guarded statements (Agents Memory and Skills § 10).
P Hand entry is not a fallback. A row typed by an agent outside a skill has no coverage map, no write log entry, no teardown and no assertion — so it is invisible to the deployment set, to the reversal of Failure and Recovery § 2 and to the donor comparison. The rule is the same one that makes the summary a generated artefact rather than prose (Configuration Module § 1b): the medium is the case, not the conversation.
P When the skill or its tooling fails, the case is still finished — and the skill is what gets fixed. The order is: diagnose, correct the skill or the tool it calls, re-run it, and let the corrected run produce the rows. A hand-written workaround that leaves the skill broken is not a completed case: the same failure returns on the next product, and the estate's knowledge of it lives only in one transcript. So a case that met a skill defect closes with three things, not one — the configured product, the amended skill or tool, and the memory entry that says what it could not do before (Agents Memory and Skills § 10, Stage Precipitation). Where the defect is not the skill's to fix — a missing endpoint, a backend constraint — it is a gate Hd to the Development module (id source), which is a result, not a workaround.
P The bound on improvising first is the one P-15 already sets (Product Configurator § P): after two failed attempts at an unknown contract the agent stops and asks the owner, rather than a third attempt or a manual substitute.
What runs this stage
| Agents | configuration.ipal.stage |
| Isolated sub-agents | configuration.verifier · configuration.estate_prober for column counts before a write |
| Memory domain it owns | configuration/ipal — catalogue dependencies, the cover↔object fan-out, factor semantics (Agents Memory) |
| Skills it runs | abacus seed at the S2→S3 boundary (the summary → catalogue proposals) · ipal configure (the write path) · policy test (Agents Memory and Skills) |
| Its branch of the graph | Configuration Module § 0 — the module's agent tree, its memory branch and its skills · the component list is Components — Configuration |
Every stage also uses the module's shared profiles — configuration.root (the only one that reaches the human), configuration.verifier (isolated, no author context) and configuration.deployer — and the platform's write_executor and write_auditor, which are what actually touch a target (Gating).
The protocol here
- Plan: the catalogue step list in dependency order — lookups and labels before the rows that use them, product before covers, covers before definitions/risks/events, factors before values before dependencies F: [product configuration — real FKs]; the cover↔object fan-out stated explicitly (one document cover legitimately becomes N rows, one per object F: [product config]); the factor set includes the object's own attributes, or the object step renders empty [F: the PC 9951 run].
- Pre-check inside the plan: is there a deployed, product-parameterizable process that fits this product's shape — established from the running system, early; "no" is a deploy boundary → gate Hd to Development (Camunda work), a legitimate result, never a forced near-fit [F: the PC 9951 run]. Object codes are effectively a closed set coupled to frontend components — a new one is frontend work (Hd) F: [field validation frameworks].
- Apply: guarded writes via the write executor; reuse platform-wide codes, scope values per product, never edit shared/global rows [F: the PC reuse-before-create rule].
- P Checks —
CFG_FLD_VALIDATION,CFG_VAL_OVERRULED_GRP,PR_OPERATIONS,CFG_MANUAL_PREMIUM_CONTROL, LD conflicts: S3 owns them; theipal configurecoverage map names the path per object (D74). - Test — the policy skill: drive quotation → issue on the working environment — product findable, steps render, dropdowns populated, covers selectable, premium renders (the same figure S2 reconciled), policy issues; assert the persisted structure against the specification.
- Iterate: every blocker found while driving is the next work item, not a result; changes reverse cleanly via the build state; the human exercises the product themselves and the loop continues until they are satisfied.
- Scripts/deploy: the stage's set joins the deployment scope (typically abacus+ipal at minimum for a new product); test-policy side effects (numbering, blanks, documents, workflow state) are planned and cleaned or explicitly retained.
The full per-column configuration model, the ten axes, the failure atlas and the ordered checklist are § PF-1…PF-10 below.
The pricing-factor stage rule P
Every factor is written with SALE_STAGE = 'POL', and only POL (decision 31.08.2026, reaffirmed 02.09.2026). The QT sale stage — and the wizard, offer-resolution and seeding code that read it — is being obsoleted; the module configures for the estate as it will be, not for the path that is going away.
Remark for agents — why you will still meet QT, and what not to do with it. The facts below are true of today's code and data, and they are not a reason to write QT rows:
- F The quotation wizard buckets factors by
saleStage === 'QT'with no fallback in either direction, so a POL-only factor typed in during a quote step renders no control and the insured-value lookup degrades silently to"IV" = 0(verified 01.09.2026,base-process-quote.component.ts:776,794). Four codes (DURATION_DIMENSION,DURATION,PAYMENT_FREQUENCY,IV_CURRENCY) are hardcoded exceptions that configuration cannot extend. - F On the Ablera dev database 23 of 76 products carry QT rows and 170 factors carry both stages; on Bulstrad QA 16 products / 186 rows; on STAGING and PROD zero — the customer estate is already POL-only in data (re-measured 03.09.2026 — the later of the two runs, and the one § PF-8b reports); live UI paths still seed
saleStage:'QT'. - F
PR_PRICING_FACTORShas the dual-stage unique key;PR_PRICING_FACTOR_ANNEXdoes not.
What the agent does with a product whose wizard has a quotation step: it does not add QT rows and does not copy a donor's. It records the dependency in the MISSING paper — quote step reads the QT bucket; resolving event: the QT obsoletion on the target branch — caps the proof level it claims at what the POL-only product actually reaches, and raises it at H2. H3 may close at that capped rung (typically priced or reachable) with the MISSING entry open; the case's report names the rung and the resolving event, and the deployment set excludes what the quotation step would need — a product is not held hostage to the obsoletion, and no QT row is written to free it (Configuration Module § 8 A). Whether the quotation step leaves the process or the wizard learns to read POL is Development-module work (gate Hd), never configuration.
The INSIS side, and why it is not a stage
P The INSIS side of a dual-system product is not configuration work (decision Vladimir, 03.09.2026 — D85). It is one PL/SQL script, run once per product — the contract rows and the seam rows the transfer needs. It carries no per-product quirks and is not expected to grow any, so it earns no sub-stage, no oracle-insis connector catalogue, no separate grant scope, no environment pairing and no teardown checklist. What the stage records is a named deliverable with an owner: the script, who runs it, on which environment, and the date. Where the script itself has to change, that is Development work at gate Hd — the same treatment a user exit gets.
P The configuration proof ladder therefore ends at issued. Whether a product actually arrives in the incumbent system is verified by the Data and information module (D144), which owns transfer investigation and its comparison mechanics; a configuration case does not claim a rung it has no connector to check. A product that must transfer is not less proved — it is proved to the edge of what this module writes, and the transfer is a Data and information case with its own evidence.
The process — registered here, authored in Development
P The BPMN process belongs to the policy product, so it is configured in this stage (decision Vladimir, 03.09.2026 — D86), not in the wiring stage. The pre-check above establishes whether a deployed process is product-parameterisable. When one is, its registration against the product is written here: the process key on the product, its task and role bindings, and the deploy-coupled items flagged for the service deployment plan. When none is — the health path is the worked example, where the product id is a constant in the plug-in — the case raises Hd to Development for the process itself and carries on with the rest of the catalogue; the registration lands when the process does.
Pricing factors — the configuration model
A pricing factor is the single most misconfigurable object in an IPAL product, and almost every way of getting it wrong is silent: an empty dropdown, a control that never renders, a value quietly replaced before pricing, a quote cheaper than the policy it becomes. The S3 IPAL author needs the whole model, not one rule at a time.
Everything here is verified against the compiled EF model and the Angular field generator on 01.09.2026, and the compiled model is authoritative F: [rule]. Where the PC extract disagrees with the estate's code, § PF-7 says so.
PF-1 The four tables
F PR_PRICING_FACTORS (25 columns on PROD; 26 on TEST/QA/DEV — § PF-7) · PR_PRICING_FACTOR_VALUES (15) · PR_PRICING_FACTOR_DEPENDENT (17) · and a fourth, PR_PRICING_FACTOR_ANNEX (5 columns; 501 rows / 42 products on PROD, 505 / 42 on STAGING, verified 03.09.2026): the per-product list of premium-relevant factors that SRD_CUST.ANNEX_PREM.cCHCover compares to decide whether a cover changed in an annex — read by PL/SQL; on master/bulstrad-qa the C# change determiner reads it too and falls back to POL-stage factors and uses QT_REQUIRED to decide requiredness when a product has no rows (T1 DB-4, T2 GIT-5). A product that supports non-cancellation annexes must list its factors here, or every annex sees the cover as unchanged. The unique key is (PRODUCT_CODE, COVER_CODE, OBJECT_CODE, PRICING_FACTOR_CODE).
F Only one foreign key exists across the three. Every other cross-reference — to a cover, an object type, a message id, a driver factor — is a free string the database will not check. This is why the failure atlas below is mostly silent failures: nothing is enforced at the constraint level.
PF-2 The ten axes
P The decisions a configurator makes, in the order they constrain each other; every constituent is F.
| # | Axis | Column(s) | What choosing it means |
|---|---|---|---|
| 1 | Identity | PRICING_FACTOR_CODE |
not free naming — it is the ABACUS parameter key and the form-control key, so a rating axis must equal the compiled grid's column name |
| 2 | Level | OBJECT_CODE |
NULL ⇒ policy-level, value lands in POL_VALUES; set ⇒ object-level and must equal the SR_OBJECT_TYPE. COVER_CODE is a weaker third level: it only scopes the diagnostic message and the IV-per-cover grouping |
| 3 | Datatype | DATATYPE |
ten accepted values. The IV family (IV, IVLIST, IV_COV, IV_COVLIST) is a scope decision, not formatting: IV/IVLIST render before the offer, IV_COV* under a cover after it |
| 4 | Sale stage | SALE_STAGE |
POL for everything the module writes; QT is the obsoleted path — see the Stage IPAL rule |
| 5 | Visibility | UI_FLAG |
§ 5 |
| 6 | Value source | VALUE_SOURCE |
§ 3 — who actually answers the question |
| 7 | Dependency | DEPENDENT_ON_FACTOR + PFD rows |
§ 4 |
| 8 | Ordering & grouping | UI_GROUP, UI_ORDER, three *_DESCR_LINK msgIds |
groups sort by code; every msgId must resolve in SR_MESSAGES |
| 9 | Default | HAS_DEFAULT_VALUE + DEFAULT_VALUE, or a value-row default, or RANGE_FROM |
precedence: factor default > value-row default; in the UI, saved data > factor default > RANGE_FROM |
| 10 | Required-ness | QT_REQUIRED |
§ 6 — the trap |
PF-3 Value source — who answers the question
F The extraction dispatch key is VALUE_SOURCE, not POLICY_TABLE (ParameterExtractorPPCS.cs:257,268-277,298-316). Three shapes:
VALUE_SOURCE |
Who answers |
|---|---|
POV / PV |
the operator, through the form |
pc.SrObjects.… |
an object attribute, read off the object record |
| an expression | computed |
F POLICY_TABLE is read in exactly two places — the clone filter and an annex null-test. It is not dispatch. Set POLICY_TABLE='POV' unless you intend the answer not to survive a renewal.
F Some features are supplied by neither the operator nor the object but by a registered backend operation — the customer-attribute injector supplies PH_ADDRAREA, PH_REGION_CODE, PH_MUNICIPALITY, PH_TYPE, PH_AGE. Those are a PR_OPERATIONS row, not a factor (Stage Abacus § SUM-5).
PF-4 Dependencies
F The correct value of DEPENDENT_ON_FACTOR is MAIN_FACTOR_CODE|form.model.path (field-generator.ts:471) — the right half is a form path, not a factor code. PR_PRICING_FACTOR_DEPENDENT then holds the admitted pairs.
F Dependency resolution is purely front-end. The premium engine never reads PR_PRICING_FACTOR_DEPENDENT, so a broken dependency cannot produce a wrong premium — only a wrong or unusable dropdown.
F An inapplicable combination is modelled with UI_FLAG/QT_REQUIRED — never by omitting dependency rows, which produces an empty-but-required control.
PF-5 Visibility — hidden versus absent
F UI_FLAG='N' means the control is not rendered, so the value is never collected. The quote then fails naming a factor that is plainly present in the table.
F The quote wizard force-includes exactly four codes regardless of UI_FLAG — DURATION_DIMENSION, DURATION, PAYMENT_FREQUENCY, IV_CURRENCY (base-process-quote.component.ts:801-812). Configuration cannot extend this list; a new code needs a code change.
PF-6 QT_REQUIRED is the rating-set gate
F It is not an asterisk on a form field. ParameterExtractorPPCS.cs:64,69 filters both candidate sets on 'Y', so 'N' excludes the factor from the premium call and from the completeness check. The failure then surfaces as an ABACUS Missing features {…} error, never as an IPAL message — and on a quotation the engine zeroes the affected groups, so the quote is silently cheaper than the policy it becomes.
P Set QT_REQUIRED='Y' for every rating axis, whatever a donor product says. It is a flat Y/N and cannot express "required only when…" — a conditional requirement in a specification is raised as an open question, never guessed.
PF-7 Where the sources are wrong
F One correction from code, and two claims that live data overturned — the difference matters, because reading code alone produced two wrong answers.
| Claim | Verdict against the database |
|---|---|
(PC extract) POLICY_TABLE is the extraction dispatch key |
Wrong — VALUE_SOURCE is. The extract predicts correctly most of the time only because both usually say POV |
(from the compiled EF model) UI_REQUIRED is absent; emitting it raises ORA-00904 |
Environment-specific (verified 03.09.2026, T1 DB-1, T2 GIT-5): absent on PROD (25 columns, unmapped on bulstrad-prod/bulstrad-staging); present NOT NULL on TEST/QA/DEV (26 columns, mapped on master/bulstrad-qa). A schema drift between environments: an insert script is environment-specific, and the plan introspects ALL_TAB_COLUMNS at plan time rather than trusting the compiled model or this page. |
(from code) STATUS is dead — no filter reads it |
Mostly NULL on factors; values carry states. Factor STATUS is mostly NULL (A only on DEV factors); value STATUS carries M/MO/O/U, with A on QA/DEV (T1 DB-1). Do not use it to retire a row — and do not assume it is ignored either |
F PRICING_TYPE has no behavioural consumer in serdica-backend on any branch — BP versus LD changes no code path (Measurements § RE § 1). F PR_PRICING_FACTOR_ANNEX is consumed — see § PF-1 — and written by nothing at runtime: it is configuration, S3's to write for any product with non-cancellation annexes.
PF-8 Failure atlas
Silent failures first — these are the ones that cost hours, because nothing errors. Every row is an F.
| Misconfiguration | Symptom |
|---|---|
DATATYPE NULL |
field never rendered, no message at all |
DATATYPE outside the ten implemented (IV_COV/IV_COVLIST are bucketed but have no case in the generator) |
console.error, returns null, filtered out — invisible to the operator |
QT_REQUIRED='N' on a rating axis |
excluded from the parameter set and the completeness check ⇒ engine Missing features; on quotes the grid returns 0.0 ⇒ quote cheaper than the policy |
VALUE_SOURCE blank |
factor silently absent from the premium request |
VALUE_SOURCE points at an object column for an operator-answered factor |
the operator's selection is discarded and the object column priced instead |
DEPENDENT_ON_FACTOR with no \| |
the onInit dependency machinery returns early, so the list is never filtered. It is not empty: options() falls through to the full store list because showDepListsValues defaults to false and no caller sets it. 137 rows are in this state on Ablera DEV; customer estate snapshot 03.09.2026: PROD 102 · TEST 103 · QA 126 (56 on active products) — a day earlier the same query returned QA 119 / PROD 98 (§ PF-8 measured 02.09.2026 below): two snapshots of a moving configuration, both kept with their dates — CAR_MODEL→CAR_MAKE, DURATION→DURATION_DIMENSION and similar, across products 4704, 4710, 4703, 3615, 2214, 1126 — so this is the estate's normal configuration today, not a defect backlog |
| dependency right-hand path wrong | the list never loads; the dropdown stays empty forever — this is the genuinely dangerous shape, because unlike a missing pipe it does not fall through to the full list |
| PFD rows missing for a driver value | empty, still-required, still-enabled dropdown — an unfillable hard stop that reads as "not applicable" |
PFD row with NULL name and no DESCR_LINK |
list returns, default applied underneath, option panel renders blank |
OBJECT_CODE ≠ the SR_OBJECT_TYPE |
in neither extractor set and no wizard bucket; nothing errors |
OBJECT_CODE NULL + stage not matching the step |
filed under objectFactors[undefined], never rendered |
UI_FLAG='N' copied from a donor onto an operator-answered factor |
value never collected; the quote fails naming a factor that is plainly in the table |
POLICY_TABLE ≠ 'POV' |
answers are not carried into a clone or renewal — a renewed policy loses them |
RANGE_FROM/_TO in BGN while values were converted to EUR |
the only option fails the in-options validator ⇒ the product prices correctly and is unsellable through the UI |
HAS_DEFAULT_VALUE='Y' with NULL DEFAULT_VALUE |
default silently dropped — both are required |
stray ATTR1 on a value row |
the value sent to the engine is silently replaced by ATTR1 |
PFV PRODUCT_CODE set where a global was intended |
the option vanishes for other products, or leaks into all of them |
two value rows with DEFAULT_VALUE='Y' |
FirstOrDefault picks arbitrarily |
DESCR_LINK with no SR_MESSAGES row |
the raw msgId is shown as the label |
PRICING_FACTOR_NAME ≠ the object-attribute dependency name |
applicability rules silently stop applying |
>1000 value/dependent rows on a branch still sending pageSize:-1 |
list silently truncated at the server cap P |
Loud failures, for completeness: PRICING_TYPE over 3 characters → ORA-12899 · a column named RANGE_FROM/RANGE_TO in a script → ORA-00904 · emitting UI_REQUIRED on PROD → ORA-00904 (the column exists on TEST/QA/DEV — § PF-7) · two HAS_CURRENCY='Y' factors on one cover → At most one IV can be defined per cover!
F Measured on the customer estate, 02.09.2026 (Measurements § DB § 1d): DEPENDENT_ON_FACTOR has two live shapes — CODE|path (44 rows on QA) and a bare CODE (119 rows on QA, 98 on PROD on 02.09.2026; 126 / 102 on the 03.09 re-run above: DURATION → DURATION_DIMENSION, the car chain) — so the reader must accept both and the "no |" row above describes the majority, not a defect; DESCR_LINK is a literal label on ~1,000 value rows per environment („Андора", „Албания"), so the SR_MESSAGES join is a validity test for new rows only; 36 (QA) / 80 (PROD) factors say HAS_DEFAULT_VALUE='Y' with no DEFAULT_VALUE; one QA row carries HAS_DEFAULT_VALUE='B'; POLICY_TABLE is a single space on 144–161 rows and DATATYPE on 2 — blank strings, not NULL, so TRIM(col) IS NULL is the empty test. The S3 validator flags all of these on new rows and never repairs existing ones.
PF-8b What the live configuration actually looks like
F Measured on the Ablera dev database (SID orcl1), 01.09.2026 — the platform's own development target is Bulstrad QA (Architecture § 1), where these queries are re-run — 76 products, 2,511 factor rows, 4,660 value rows, 16,400 dependency rows:
| Sale stage | 2,261 POL against 250 QT. POL is overwhelmingly the norm |
| Products with any QT row | 23 of 76. The other 53 are POL-only end to end |
| Factors carrying both stages | 170 — the dual-row pattern the unique key exists for, used deliberately |
| Products using QT most | 4704 (52), 9951 (40), 4710 (33), 3407 (24), 1126 (20) |
What this measures is the Ablera development database, not the customer estate. Re-measured 03.09.2026 on Bulstrad QA, STAGING/TEST and PROD (T1 DB-2): STAGING/TEST (1 988 factor rows / 58 products) and PROD (1 977 / 57) carry SALE_STAGE='POL' only — zero QT rows, zero dual-stage factors, on every product; QA carries 186 QT rows over 16 products (4704 40, 4710 33, 3407 24, 1126 20 …); product 9951 does not exist on QA. The POL-only rule (Stage IPAL) is therefore already the data state of the two customer-facing environments; the obsoletion work is 186 rows on QA plus the code that reads the QT bucket. The dev numbers above stay as the record of what a development database can mislead one into.
P What the dev database still cannot answer — the customer-estate half is settled two lines above: whether any of the 53 POL-only products would break if a quote step were added to them.
PF-9 The order to decide in
P A stage author configuring one factor works this list top to bottom:
- Is it a factor at all? Limits and deductibles are
PR_BASIC_COVER_DEF, not a pricing factor. - Code — equal to the compiled grid's column name if it is a rating axis.
- Level —
OBJECT_CODENULL or exactly the object type; thenCOVER_CODE. - Datatype — one of the ten; the IV family is a scope decision.
- Sale stage —
POL, always (Stage IPAL); a quote-step dependency is a MISSING entry, never a QT row. - Who answers it —
VALUE_SOURCE, never blank;POLICY_TABLE='POV'unless the answer should not survive a renewal. UI_FLAG—Yonly if the operator answers it. Never copy a donor's visibility without its rating shape.QT_REQUIRED='Y'for every rating axis, whatever the donor says.- Values — global or product-scoped; exactly one default; every row needs a name or a resolvable msgId; leave
ATTR1empty unless substitution is intended. Where an existing list is being extended, read its current maximumUI_ORDERfirst and append, or the new values sort into the middle of the dropdown. - Dependency — both halves of the pipe, then a row for every driver value.
- Default —
HAS_DEFAULT_VALUE='Y'and a non-null default, or neither. - Presentation — group, order, and three msgIds that exist in
SR_MESSAGES. - Cross-check the range against the value list — for any
IVLIST, confirm the range admits at least one listed value. This is the currency-changeover trap. Where the two numbers reconcile through a division by the fixed BGN→EUR rate, it is the half-finished changeover and the range is the stale side — the values are already in EUR [F: the PC 9951 run]. - Storage binding — the factor is bound to the column its answer is written to, in
PR_PRACING_FACTOR_DBFLD(sic — the typo is in the schema, and it is why this step gets forgotten) [F: the PC 9951 run; Schema Quirks § 4].
PF-10 The object side, and the untyped slots
P Three things sit next to the factor model rather than in it, and each is a place a requirement lands that the ten axes do not cover.
F OBJECTS_CONFIG is a factor code, not a table. Its values are the object codes a product offers; their STATUS carries M/O/U/MO and a product needs at least one M or MO or the object step renders empty; ATTR2 = 1 limits the product to a single object [F: the PC 9951 run].
F ⚠ ATTR3 is untyped, so its meaning is set by whoever populated it. On most factors it is a per-row role gate — a payment-frequency value restricted to an instalment role — which makes it a second place a "who may see this" requirement can land, beside the route's allowed roles (Stage Serdica). On OBJECTS_CONFIG it is not a role at all: it holds the endpoint path that registers that object on a quotation, in <Section>/<Command>/<action> form, and it differs per object type [F: the PC 9951 run].
P The general rule is worth more than the column: before filling any ATTR* slot, read what live products carrying the same factor put there — never fill one by analogy with a different factor. The same caution as the ATTR1 substitution trap in § PF-8, one level up.
F Object-attribute visibility is OBJ_ATTR_DEPENDENCY — six columns, all NOT NULL. ⚠ The controlling pair is the object's subtype, not an arbitrary attribute: the table expresses "for this subtype of object, is that attribute applicable", which is narrower than its name suggests. An object attribute is made mandatory on the factor instead, with QT_REQUIRED='Y' and the attribute name as PRICING_FACTOR_NAME [F: the PC 9951 run]. F A PRICING_FACTOR_NAME that does not match the attribute name silently stops the applicability rules applying — the same row already in the § PF-8 atlas.
Facts to hold F
- Deductibles/limits live where the product family keeps them — read the live precedent of the family, never assume.
- Every visible name resolves
DESCR_LINK → SR_MESSAGES(_LNG)per maintained language; cloning copies the pointer — new labels need new codes, or the new product renders the donor's wording (Translations carries the chain and the sweep's three assertions; serdica stage owns the sweep). - A code that exists is not a row this product has: every factor needs its own
PR_PRICING_FACTORSrow, universal ones included, and a factor code whose values are global cannot be re-scoped — reusing it merges two value lists (Shared Vocabulary). - A factor's source must resolve locally; a remote one is refused, never pinned to a constant (Factor Sources).
- Reconcile the document's covers against distinct cover codes, never row counts.
Real material — what live cases asked of this stage
Eight configuration cases were walked against this design (Configuration Module § 8 carries each case in full). What each one demanded here:
CS-1 · 9951 «Каско Максимум» replayed through v2 — The PC agent built 9951 on Ablera dev from a 13-sheet template (Продукт · Тарифни групи · Клаузи · Покрития · Зависимости · Оферти · Правила за премия · ЗС/лимити · Подписвачески въпросник 91 rows · Зависимости между въпроси 65 rows · Такси · Ref) and a 10-page tariff PDF (raздели I–VIII).
Plan in FK order (lookups → product → covers → definitions/risks/events → factors → values → dependencies); the CAR object attributes (make, model, first registration, seats, engine volume) copied as codes from 4704, own rows for 9951; process pre-check: VehicleCascoPolicyWithPhotos accepts an arbitrary productCode on dev (extract: „re-test the candidate set per environment"). Ten axes per factor; SALE_STAGE = 'POL' throughout. The POL-only remark bites immediately: 9951's wizard has a quotation step and the donor carries 40 QT factors (§ PF-8b). The stage writes no QT rows, records „quote step reads the QT bucket; resolving event: QT obsoletion on the target branch" in MISSING, caps the proof level at priced and says so at H2. PR_OPERATIONS rows for the minimum premium (Stage Abacus § SUM-2 — PREMIUM_MIN is not compiled). Policy skill: product findable, steps render, dropdowns populated — but the quote step renders no factor controls because the wizard buckets on QT. H3 may close at the capped rung (priced) with the MISSING entry open; the resolving event is Development work on the QT obsoletion.
CS-3 · HelpDesk 00097807 — 3607 „трудова злополука": new clause, loading, minimum premium — Bulstrad (Зорница Йорданова, 16.03.2026 onward) asks for: (1) +50 % loading when ≤5 insured persons, counted across individually entered persons and list counts; (2) minimum premium 30 EUR per policy, message at proposal level, „без опция да се конвертира в полица"; (3) a new optional clause „Временна загуба на работоспособност от избрана ЗС", limit free up to 3 000 EUR, rate 0.15 % — changed mid-thread by the board to 5 000 EUR and 0.1 %; later (4) all groups on one policy must carry identical covers; (5) the ЗС of the new clause must be identical across groups; (6) the short-term tariff split into ticket 102052; (7) override of the minimum only for named users (Зорница, Валя, Захариев) — later withdrawn to „message + proposal stays proposal, only we may convert".
New PR_BASIC_COVERS row(s) per object (PERSON, and the group object), names, definition (LIMIT 0–5 000 EUR V), risk/event rows, PR_BASIC_LD for the loading with the shortened code, LT_LD_CODES (LdConflicts throws at confirmation otherwise — skill trap), PR_OPERATIONS for the 30 EUR minimum with the „proposal saves, policy does not" semantic — which is an operation class selected by a row; if no class implements „block conversion, allow proposal", it is Hd. Requirements (4)/(5) → CFG_FLD_VALIDATION + CFG_VAL_OVERRULED_GRP, or an operation. [closed by § The protocol here] S3 owns checks and the ipal configure coverage map names the path per object.
CS-4 · HelpDesk 00098747 — 2214/2215 promo „65 години Булстрад": pay 60 months, get 65 — Home products 2214/2215: for the campaign, term 65 months, premium for 60; everything else unchanged.
One PR_PRICING_FACTOR_DEPENDENT row (DURATION under M = 65). Shared or product-scoped? § PF-8: „PFV PRODUCT_CODE set where a global was intended — the option vanishes for other products, or leaks into all of them" — the dependency row for DURATION is often global; scoping it to 2214/2215 is P-17 reuse-before-create. [ok], the rule exists.
CS-5 · Product 8000 „Здравна грижа" — packages × levels × options, cover modes, a Camunda process, CG-9 — Group health, EUR, QA only (10.239.82.106), 10 UI covers + 52 level-variant child rows (IP_O/IP_L/IP_E), consumption modes PRV/REIM per cover (PREVENTION and EXTRA_CARE PRV-only), tariff per member × package × level × option with ordered corrections (sector, group size, age band, −5 % commercial), Профилактика individually rated (no rate), Критични заболявания only above 150 persons, claims-side ABACUS templates SRVPRICE# and CLMIND#, BPMN in Core.Plugin.Bulstrad, HealthBasicProductId=8000 hardcoded.
Object PERSON + group; factors at group level (sector, size, level per package); POL_COVERS.COVER_PACKAGE is NULL on every QA row (anatomy memo) — the level a member actually holds is not locatable; S3's policy skill „asserts the persisted structure against the specification" and would fail or, worse, pass on the wrong column. [ok] in spirit (CR-3 honest level), but the skill needs the health join rule (cover_order, not cover_code) — memory domain configuration/ipal seed.
CS-6 · Cargo 1101/1102 — a product that transfers to INSIS, with no INSIS structure in the spec — Bulstrad asks to add HSSC (HelpDesk 00102452 „Добавяне на HSSC по всички кодове по Карго") — a new ICC cover across 1100/1101/1102/1103 — and, separately, a new print variant.
PR_BASIC_COVERS rows per product × cargo object, names, risks/events; LDs none. Policy skill: issue a 1102 under a TEST framework — the child number comes from the framework series, so the test policy consumes a counter in a shared series that INSIS-direct 1102s also consume (SD-1904 collision). Effect class: irreversible (numbering). [ok] Failure and Recovery § 2 names consumed numbering; the S3 plan must say so before H2.
Challenges
- P Lifecycle scope per assignment (annexes, renewal, cancellation, framework children) — a plan decision with the human; the skill tests what was declared.
- P The confirming database/source sweeps of 03.09.2026 establish the live distribution of
DATATYPE,VALUE_SOURCE,QT_REQUIRED,SALE_STAGE,STATUS,UI_REQUIREDand_ANNEXconsumers (T1 DB-1/3/4, T2 GIT-5). - P The
pageSize:-1truncation cap was not read this session — the branch difference is confirmed, the cap value is not.