Source contract and expected records
This is an original, fully synthetic mapping example. Inputs are plain, acyclic JSON-shaped records with the fields shown; the canonical shape and rejection codes belong to this example. Adapt the contract before using a different source.
The original tree has nonempty text outcome leaves or nodes containing if, then and else. if names a Boolean condition. Recursively carry true/false conditions down each branch; reject a missing branch or a field repeated along one path. An unconditional text outcome becomes one path with no conditions.
| Case | Input record (JSON) | Expected output (JSON) |
|---|---|---|
| Flatten original branch tree | {"tree":{"if":"urgent","then":"fast","else":{"if":"special","then":"specialist","else":"normal"}}} | {"ok":true,"value":{"paths":[{"conditions":[{"field":"urgent","is":true}],"outcome":"fast"},{"conditions":[{"field":"urgent","is":false},{"field":"special","is":true}],"outcome":"specialist"},{"conditions":[{"field":"urgent","is":false},{"field":"special","is":false}],"outcome":"normal"}]}} |
| Reject missing branch | {"tree":{"if":"urgent","then":"fast"}} | {"ok":false,"error":"TREE_NODE"} |
| Preserve unconditional outcome | {"tree":"manual"} | {"ok":true,"value":{"paths":[{"conditions":[],"outcome":"manual"}]}} |
Run the local transformation
Run the complete example as a JavaScript module in Node.js. It contains the pure mapping function, all three inputs and literal expected outputs; assertions stop on a mismatch. A rejected case returns ok=false with an error code, rather than a partial canonical record.
import assert from 'node:assert/strict';
function decisionPaths(i){
const fail=error=>({ok:false,error});if(!i||!Object.hasOwn(i,'tree'))return fail('TREE_MISSING');
const paths=[];let error;
const walk=(node,conditions,used)=>{
if(typeof node==='string'){if(!node){error='TREE_OUTCOME';return;}paths.push({conditions,outcome:node});return;}
if(!node||typeof node!=='object'||Array.isArray(node)||typeof node.if!=='string'||!node.if||!Object.hasOwn(node,'then')||!Object.hasOwn(node,'else')){error='TREE_NODE';return;}
if(used.has(node.if)){error='TREE_REPEAT';return;}
const next=new Set(used);next.add(node.if);
walk(node.then,[...conditions,{field:node.if,is:true}],next);
walk(node.else,[...conditions,{field:node.if,is:false}],next);
};
walk(i.tree,[],new Set());return error?fail(error):{ok:true,value:{paths}};
}
// Original synthetic cases; expected outputs are literal records.
const cases = [
{
"label": "Flatten original branch tree",
"input": {
"tree": {
"if": "urgent",
"then": "fast",
"else": {
"if": "special",
"then": "specialist",
"else": "normal"
}
}
},
"expected": {
"ok": true,
"value": {
"paths": [
{
"conditions": [
{
"field": "urgent",
"is": true
}
],
"outcome": "fast"
},
{
"conditions": [
{
"field": "urgent",
"is": false
},
{
"field": "special",
"is": true
}
],
"outcome": "specialist"
},
{
"conditions": [
{
"field": "urgent",
"is": false
},
{
"field": "special",
"is": false
}
],
"outcome": "normal"
}
]
}
}
},
{
"label": "Reject missing branch",
"input": {
"tree": {
"if": "urgent",
"then": "fast"
}
},
"expected": {
"ok": false,
"error": "TREE_NODE"
}
},
{
"label": "Preserve unconditional outcome",
"input": {
"tree": "manual"
},
"expected": {
"ok": true,
"value": {
"paths": [
{
"conditions": [],
"outcome": "manual"
}
]
}
}
}
];
for (const sample of cases) {
const actual = decisionPaths(sample.input);
assert.deepEqual(actual, sample.expected);
console.log(sample.label, JSON.stringify(actual));
}
Work through the boundary
Which conditions reach specialist in the first case?
Show the worked answer
urgent=false followed by special=true. fast requires urgent=true; normal requires both urgent=false and special=false. The three paths describe the supplied tree rather than executing a decision.