@pdtf/schemas 3.6.0-dev.7 → 3.6.0-dev.8
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/busy-spence-79c6bf/CLAUDE.md +101 -0
- package/busy-spence-79c6bf/LICENSE +21 -0
- package/busy-spence-79c6bf/README.md +435 -0
- package/busy-spence-79c6bf/docs/branch-release-workflow.md +138 -0
- package/busy-spence-79c6bf/docs/sef25-extensions-ui-spec.md +355 -0
- package/busy-spence-79c6bf/index.js +654 -0
- package/busy-spence-79c6bf/package-lock.json +7299 -0
- package/busy-spence-79c6bf/package.json +44 -0
- package/busy-spence-79c6bf/scripts/ta6ed6-titles.js +403 -0
- package/busy-spence-79c6bf/scripts/ta7ed5-titles.js +183 -0
- package/busy-spence-79c6bf/src/examples/v1/exampleAddParticipantVouch.json +32 -0
- package/busy-spence-79c6bf/src/examples/v1/exampleDocumentedVouch.json +40 -0
- package/busy-spence-79c6bf/src/examples/v1/exampleElectronicRecord.json +30 -0
- package/busy-spence-79c6bf/src/examples/v1/exampleTransaction.json +878 -0
- package/busy-spence-79c6bf/src/examples/v1/exampleVouch.json +25 -0
- package/busy-spence-79c6bf/src/examples/v2/exampleTransaction.json +901 -0
- package/busy-spence-79c6bf/src/examples/v3/exampleAddParticipantVouch.json +38 -0
- package/busy-spence-79c6bf/src/examples/v3/exampleCustomOverlay.json +55 -0
- package/busy-spence-79c6bf/src/examples/v3/exampleCustomOverlay2.json +93 -0
- package/busy-spence-79c6bf/src/examples/v3/exampleDocumentedVouch.json +47 -0
- package/busy-spence-79c6bf/src/examples/v3/exampleElectronicRecord.json +36 -0
- package/busy-spence-79c6bf/src/examples/v3/exampleTransaction.json +1038 -0
- package/busy-spence-79c6bf/src/examples/v3/exampleVouch.json +32 -0
- package/busy-spence-79c6bf/src/mappings/SendPropertyDetailsRequestSchema.json +1588 -0
- package/busy-spence-79c6bf/src/mappings/adf.js +198 -0
- package/busy-spence-79c6bf/src/schemas/v1/GeoJSON.json +1279 -0
- package/busy-spence-79c6bf/src/schemas/v1/baspiNotes.html +102 -0
- package/busy-spence-79c6bf/src/schemas/v1/energy-performance-certificate.json +124 -0
- package/busy-spence-79c6bf/src/schemas/v1/freehold-information.json +2051 -0
- package/busy-spence-79c6bf/src/schemas/v1/leasehold-information.json +2064 -0
- package/busy-spence-79c6bf/src/schemas/v1/legal-information.json +1045 -0
- package/busy-spence-79c6bf/src/schemas/v1/material-facts.json +2109 -0
- package/busy-spence-79c6bf/src/schemas/v1/pdtf-transaction-search-results.json +40 -0
- package/busy-spence-79c6bf/src/schemas/v1/pdtf-transaction.json +191 -0
- package/busy-spence-79c6bf/src/schemas/v1/searches/drainage-and-water.json +261 -0
- package/busy-spence-79c6bf/src/schemas/v1/searches/local-land-charges.json +69 -0
- package/busy-spence-79c6bf/src/schemas/v1/searches/local-searches-required.json +857 -0
- package/busy-spence-79c6bf/src/schemas/v1/searches.json +237 -0
- package/busy-spence-79c6bf/src/schemas/v1/title-deed.json +841 -0
- package/busy-spence-79c6bf/src/schemas/v2/baspiNotes.html +102 -0
- package/busy-spence-79c6bf/src/schemas/v2/combined.json +37731 -0
- package/busy-spence-79c6bf/src/schemas/v2/overlays/baspi.json +8132 -0
- package/busy-spence-79c6bf/src/schemas/v2/overlays/con29DW.json +705 -0
- package/busy-spence-79c6bf/src/schemas/v2/overlays/con29R.json +2956 -0
- package/busy-spence-79c6bf/src/schemas/v2/overlays/fme1.json +1259 -0
- package/busy-spence-79c6bf/src/schemas/v2/overlays/llc1.json +25 -0
- package/busy-spence-79c6bf/src/schemas/v2/overlays/lpe1.json +2171 -0
- package/busy-spence-79c6bf/src/schemas/v2/overlays/nts.json +326 -0
- package/busy-spence-79c6bf/src/schemas/v2/overlays/oc1.json +2799 -0
- package/busy-spence-79c6bf/src/schemas/v2/overlays/piq.json +3419 -0
- package/busy-spence-79c6bf/src/schemas/v2/overlays/rds.json +3736 -0
- package/busy-spence-79c6bf/src/schemas/v2/overlays/ta10.json +5306 -0
- package/busy-spence-79c6bf/src/schemas/v2/overlays/ta6.json +4533 -0
- package/busy-spence-79c6bf/src/schemas/v2/overlays/ta7.json +1045 -0
- package/busy-spence-79c6bf/src/schemas/v2/pdtf-transaction.json +34351 -0
- package/busy-spence-79c6bf/src/schemas/v2/skeleton.json +3973 -0
- package/busy-spence-79c6bf/src/schemas/v3/combined.json +48643 -0
- package/busy-spence-79c6bf/src/schemas/v3/compactSkeleton.txt +4519 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/README.md +299 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/baspi4.json +8030 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/baspi5.json +8180 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/con29DW.json +716 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/con29R.json +2981 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/extensions/ac.json +334 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/extensions/as.json +74 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/extensions/dk.json +33 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/extensions/dr.json +73 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/extensions/er.json +85 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/extensions/fd.json +75 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/extensions/hi.json +51 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/extensions/hs.json +73 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/extensions/ic.json +94 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/extensions/jk.json +96 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/extensions/la.json +85 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/extensions/lc.json +76 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/extensions/ma.json +90 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/extensions/mc.json +38 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/extensions/mi.json +70 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/extensions/nd.json +44 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/extensions/oa.json +24 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/extensions/oc.json +57 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/extensions/pc.json +38 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/extensions/ph.json +195 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/extensions/rw.json +69 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/extensions/sb.json +73 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/extensions/sc.json +133 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/extensions/sd.json +64 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/extensions/sf.json +74 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/extensions/sl.json +68 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/extensions/ta.json +204 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/extensions/tf.json +103 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/extensions/tr.json +74 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/extensions/wg.json +351 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/fme1.json +1266 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/llc1.json +25 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/lpe1.json +2199 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/nts.json +2159 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/nts2.json +2744 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/ntsl.json +1815 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/ntsl2.json +2219 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/oc1.json +2817 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/piq.json +3324 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/rds.json +3668 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/sef25.json +1653 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/sr24.json +38 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/ta10.json +4645 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/ta6.json +4572 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/ta6ed6.json +6665 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/ta7.json +1537 -0
- package/busy-spence-79c6bf/src/schemas/v3/overlays/ta7ed5.json +1585 -0
- package/busy-spence-79c6bf/src/schemas/v3/pdtf-transaction.json +41570 -0
- package/busy-spence-79c6bf/src/schemas/v3/skeleton.json +5767 -0
- package/busy-spence-79c6bf/src/schemas/verifiedClaims/README.md +21 -0
- package/busy-spence-79c6bf/src/schemas/verifiedClaims/pdtf-verified-claims.json +447 -0
- package/busy-spence-79c6bf/src/utils/compactSkeleton.js +84 -0
- package/busy-spence-79c6bf/src/utils/countTokens.js +67 -0
- package/busy-spence-79c6bf/src/utils/extractExtensionOverlays.js +633 -0
- package/busy-spence-79c6bf/src/utils/extractOverlay.js +381 -0
- package/busy-spence-79c6bf/src/utils/extractProperties.js +44 -0
- package/busy-spence-79c6bf/src/utils/listPathTitles.js +36 -0
- package/busy-spence-79c6bf/src/utils/minimalSkeleton.js +85 -0
- package/busy-spence-79c6bf/src/utils/pathList.csv +903 -0
- package/busy-spence-79c6bf/src/utils/pathSkeleton.js +181 -0
- package/busy-spence-79c6bf/ta6ed6-titles-plan.md +118 -0
- package/busy-spence-79c6bf/ta7ed5-titles-spec.md +89 -0
- package/package.json +1 -1
- package/src/schemas/v3/combined.json +225 -47
- package/src/schemas/v3/overlays/llc1.json +1 -2
- package/src/schemas/v3/overlays/ta6ed6.json +204 -75
- package/src/schemas/v3/overlays/ta7ed5.json +54 -20
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const traverse = require("traverse");
|
|
3
|
+
|
|
4
|
+
const combinedSchema = require("../schemas/v3/combined.json");
|
|
5
|
+
|
|
6
|
+
// Extract fields that have overlay-specific properties
|
|
7
|
+
const extractFields = [
|
|
8
|
+
"baspi4", "baspi5", "nts", "nts2", "ntsl", "ntsl2",
|
|
9
|
+
"ta6", "ta7", "ta7ed5", "ta10", "lpe1", "fme1", "piq",
|
|
10
|
+
"con29R", "con29DW", "llc1", "rds", "oc1", "sr24",
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
// Delete properties we don't want in the skeleton
|
|
14
|
+
const deleteProperties = (sourceSchema, propertyNames) => {
|
|
15
|
+
traverse(sourceSchema).forEach(function (element) {
|
|
16
|
+
if (propertyNames.includes(this.key)) {
|
|
17
|
+
this.delete(true);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
return sourceSchema;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// Collect all paths in path notation
|
|
24
|
+
const collectPaths = (schema, currentPath = "") => {
|
|
25
|
+
const paths = [];
|
|
26
|
+
|
|
27
|
+
if (!schema || typeof schema !== 'object') {
|
|
28
|
+
return paths;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Handle arrays
|
|
32
|
+
if (schema.type === "array" && schema.items) {
|
|
33
|
+
const arrayPath = currentPath + "[]";
|
|
34
|
+
paths.push(arrayPath);
|
|
35
|
+
// Continue with items
|
|
36
|
+
const itemPaths = collectPaths(schema.items, arrayPath);
|
|
37
|
+
paths.push(...itemPaths);
|
|
38
|
+
return paths;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Handle primitives - add type suffix
|
|
42
|
+
if (schema.type && !schema.properties && !schema.oneOf && !schema.items) {
|
|
43
|
+
let typeChar = "";
|
|
44
|
+
switch(schema.type) {
|
|
45
|
+
case "string": typeChar = ":s"; break;
|
|
46
|
+
case "number": typeChar = ":n"; break;
|
|
47
|
+
case "integer": typeChar = ":i"; break;
|
|
48
|
+
case "boolean": typeChar = ":b"; break;
|
|
49
|
+
default: typeChar = "";
|
|
50
|
+
}
|
|
51
|
+
if (currentPath) {
|
|
52
|
+
paths.push(currentPath + typeChar);
|
|
53
|
+
}
|
|
54
|
+
return paths;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Handle objects with properties
|
|
58
|
+
if (schema.properties) {
|
|
59
|
+
// If this is an object without a path yet, don't add it
|
|
60
|
+
if (currentPath && !schema.type?.includes("array")) {
|
|
61
|
+
paths.push(currentPath);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
Object.keys(schema.properties).forEach((propName) => {
|
|
65
|
+
const newPath = currentPath ? `${currentPath}.${propName}` : propName;
|
|
66
|
+
const propPaths = collectPaths(schema.properties[propName], newPath);
|
|
67
|
+
paths.push(...propPaths);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Handle oneOf - collect all possible properties
|
|
72
|
+
if (schema.oneOf) {
|
|
73
|
+
const variantProps = new Set();
|
|
74
|
+
schema.oneOf.forEach((oneOfSchema) => {
|
|
75
|
+
if (oneOfSchema.properties) {
|
|
76
|
+
Object.keys(oneOfSchema.properties).forEach((propName) => {
|
|
77
|
+
variantProps.add(propName);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Process each unique property
|
|
83
|
+
variantProps.forEach((propName) => {
|
|
84
|
+
const newPath = currentPath ? `${currentPath}.${propName}` : propName;
|
|
85
|
+
// Check if all variants have the same type
|
|
86
|
+
let commonType = null;
|
|
87
|
+
let isVariant = false;
|
|
88
|
+
|
|
89
|
+
schema.oneOf.forEach((oneOfSchema) => {
|
|
90
|
+
if (oneOfSchema.properties && oneOfSchema.properties[propName]) {
|
|
91
|
+
const propType = oneOfSchema.properties[propName].type;
|
|
92
|
+
if (commonType === null) {
|
|
93
|
+
commonType = propType;
|
|
94
|
+
} else if (commonType !== propType) {
|
|
95
|
+
isVariant = true;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
if (isVariant || !commonType) {
|
|
101
|
+
paths.push(newPath + ":v"); // variant
|
|
102
|
+
} else {
|
|
103
|
+
// Process the first variant's version of this property
|
|
104
|
+
for (const oneOfSchema of schema.oneOf) {
|
|
105
|
+
if (oneOfSchema.properties && oneOfSchema.properties[propName]) {
|
|
106
|
+
const propPaths = collectPaths(oneOfSchema.properties[propName], newPath);
|
|
107
|
+
paths.push(...propPaths);
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Empty objects
|
|
116
|
+
if (currentPath && !schema.properties && !schema.oneOf && !schema.items && !schema.type) {
|
|
117
|
+
paths.push(currentPath);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return paths;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
// Remove duplicates
|
|
124
|
+
const deduplicatePaths = (paths) => {
|
|
125
|
+
return [...new Set(paths)];
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// Sort paths for readability
|
|
129
|
+
const sortPaths = (paths) => {
|
|
130
|
+
return paths.sort((a, b) => {
|
|
131
|
+
// Sort by depth first (fewer dots = higher up)
|
|
132
|
+
const depthA = (a.match(/\./g) || []).length;
|
|
133
|
+
const depthB = (b.match(/\./g) || []).length;
|
|
134
|
+
if (depthA !== depthB) return depthA - depthB;
|
|
135
|
+
|
|
136
|
+
// Then alphabetically
|
|
137
|
+
return a.localeCompare(b);
|
|
138
|
+
});
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// Create clean schema
|
|
142
|
+
const coreSchema = deleteProperties(JSON.parse(JSON.stringify(combinedSchema)), [
|
|
143
|
+
"discriminator",
|
|
144
|
+
...extractFields.map((item) => `${item}Ref`),
|
|
145
|
+
...extractFields.map((item) => `${item}Required`),
|
|
146
|
+
...extractFields.map((item) => `${item}Title`),
|
|
147
|
+
...extractFields.map((item) => `${item}Description`),
|
|
148
|
+
...extractFields.map((item) => `${item}Enum`),
|
|
149
|
+
]);
|
|
150
|
+
|
|
151
|
+
// Remove metadata
|
|
152
|
+
const skeletonSchema = deleteProperties(coreSchema, [
|
|
153
|
+
"$schema", "$id", "title", "description", "required",
|
|
154
|
+
"enum", "minItems", "minLength", "format", "minimum", "maximum",
|
|
155
|
+
]);
|
|
156
|
+
|
|
157
|
+
// Collect paths
|
|
158
|
+
const allPaths = collectPaths(skeletonSchema);
|
|
159
|
+
const uniquePaths = deduplicatePaths(allPaths);
|
|
160
|
+
const sortedPaths = sortPaths(uniquePaths);
|
|
161
|
+
|
|
162
|
+
// Write as text file
|
|
163
|
+
const pathContent = sortedPaths.join('\n');
|
|
164
|
+
fs.writeFileSync("../schemas/v3/pathSkeleton.txt", pathContent);
|
|
165
|
+
|
|
166
|
+
// Also create a JSON version for comparison
|
|
167
|
+
fs.writeFileSync("../schemas/v3/pathSkeleton.json", JSON.stringify(sortedPaths, null, 2));
|
|
168
|
+
|
|
169
|
+
console.log(`Path skeleton written to ../schemas/v3/pathSkeleton.txt`);
|
|
170
|
+
console.log(`Total paths: ${sortedPaths.length}`);
|
|
171
|
+
console.log(`Text size: ${pathContent.length} bytes`);
|
|
172
|
+
|
|
173
|
+
// Count tokens
|
|
174
|
+
try {
|
|
175
|
+
const GPT3Encoder = require('gpt-3-encoder');
|
|
176
|
+
const encoded = GPT3Encoder.encode(pathContent);
|
|
177
|
+
console.log(`Tokens (GPT-3): ${encoded.length}`);
|
|
178
|
+
console.log(`Reduction: ${((1 - encoded.length / 78734) * 100).toFixed(1)}% fewer tokens than JSON skeleton`);
|
|
179
|
+
} catch (e) {
|
|
180
|
+
console.log(`Estimated tokens: ~${Math.ceil(pathContent.length / 4)}`);
|
|
181
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Plan: Exact TA6 6th-edition field titles (`ta6ed6Title`) in combined.json
|
|
2
|
+
|
|
3
|
+
## Context
|
|
4
|
+
|
|
5
|
+
The Law Society requires that when a TA6 form is rendered digitally, field labels match the
|
|
6
|
+
published form **exactly**. The PDTF schema already carries form-specific labels for other forms
|
|
7
|
+
via `<form>Title` properties (e.g. `ta6Title`, `ntsTitle`). The TA6 6th-edition (2025) layer was
|
|
8
|
+
recently added: `combined.json` now has **537 `ta6ed6Ref`** markers identifying which schema
|
|
9
|
+
properties belong to the 6th-edition form, and **51 `ta6ed6Title`** values already applied. The
|
|
10
|
+
remaining 6th-edition fields still fall back to the generic base `title`, whose wording differs
|
|
11
|
+
from the Law Society form.
|
|
12
|
+
|
|
13
|
+
This change completes the `ta6ed6Title` coverage so that **every** 6th-edition field whose form
|
|
14
|
+
wording differs from the base `title` carries its exact Law Society label, verified against the
|
|
15
|
+
source form PDF.
|
|
16
|
+
|
|
17
|
+
## Architecture (verified)
|
|
18
|
+
|
|
19
|
+
- **Source of truth:** `src/schemas/v3/combined.json` is hand-edited. It is the only place the 51
|
|
20
|
+
existing `ta6ed6Title` values live (the `ta6ed6.json` overlay has the 537 refs but 0 titles —
|
|
21
|
+
it is simply stale).
|
|
22
|
+
- **Generated artifacts:** `src/utils/extractOverlay.js` reads `combined.json` and regenerates the
|
|
23
|
+
per-form overlay files (incl. `overlays/ta6ed6.json`) and the core schema
|
|
24
|
+
(`pdtf-transaction.json`). Its `extractFields` list already includes `"ta6ed6"`, so it extracts
|
|
25
|
+
`ta6ed6Ref`, `ta6ed6Title`, `ta6ed6Required`, `ta6ed6Enum`, `ta6ed6Type`, etc. Running it after
|
|
26
|
+
editing propagates the new titles into the overlay.
|
|
27
|
+
- **Field identity:** a property belongs to the 6th-edition form iff it has a `ta6ed6Ref`. The ref
|
|
28
|
+
value (e.g. `10.2`, `5.9a`) matches the `ref` keys in the extracted form file
|
|
29
|
+
`tmp/ta6ed6-fields.json`, which was validated against the PDF (e.g. ref `10.2` →
|
|
30
|
+
"Is a permit required for on-road parking?").
|
|
31
|
+
|
|
32
|
+
## Authoritative title sources
|
|
33
|
+
|
|
34
|
+
1. `tmp/TA6 Form 6th edition.pdf` — the Law Society source form (20 pages, text-based). **The
|
|
35
|
+
final authority** per the user's "re-verify all against PDF" decision.
|
|
36
|
+
2. `tmp/ta6ed6-fields.json` — clean extraction keyed by `ref` (16 sections, ~195 labelled fields).
|
|
37
|
+
Used as the working map; every title applied must be reconciled against the PDF text.
|
|
38
|
+
|
|
39
|
+
## House style for titles (from `tmp/ta6ed6-mapping-report.md` §C.5 — matches the existing 51)
|
|
40
|
+
|
|
41
|
+
A `ta6ed6Title` is the exact form question/field text, with:
|
|
42
|
+
1. Sub-question prefixes stripped — `"(a) "`, `"(b) "`, `"(i) "`, `"(ii) "` (hierarchy is carried
|
|
43
|
+
by `ta6ed6Ref` such as `5.6a`, `5.6g(i)`).
|
|
44
|
+
2. Conditional prefixes stripped — `"If yes, "`, `"If no, "` (implicit from the `oneOf` branch).
|
|
45
|
+
3. First letter capitalised after any stripping.
|
|
46
|
+
Otherwise verbatim — punctuation, casing, and wording must match the form character-for-character.
|
|
47
|
+
|
|
48
|
+
## Scope decision (confirmed with user)
|
|
49
|
+
|
|
50
|
+
- **Base branch:** rebase this work onto `dev` (which holds the 537 refs + 51 titles + the
|
|
51
|
+
`ta6ed6.json` overlay). This worktree is currently off `master`, which has none of the ta6ed6
|
|
52
|
+
layer. PR target: `dev`.
|
|
53
|
+
- **Which fields:** add `ta6ed6Title` **only where the 6th-edition label differs** from the
|
|
54
|
+
property's existing base `title`. Skip fields whose base `title` already matches the form.
|
|
55
|
+
Also re-verify the existing 51 against the PDF and correct any that are wrong.
|
|
56
|
+
- **Sub-components** (`yesNo`, `.1`/`.2` answer parts that have no distinct printed label) inherit
|
|
57
|
+
the parent question label and do **not** receive their own `ta6ed6Title`.
|
|
58
|
+
|
|
59
|
+
## Steps
|
|
60
|
+
|
|
61
|
+
### 1. Rebase onto dev
|
|
62
|
+
The worktree branch `claude/busy-spence-79c6bf` equals `master` (clean, no work commits), so:
|
|
63
|
+
`git reset --hard dev`. Confirm `combined.json` then shows 537 `ta6ed6Ref` / 51 `ta6ed6Title`.
|
|
64
|
+
|
|
65
|
+
### 2. Build the canonical title map
|
|
66
|
+
- Read the full PDF (pages 1–20) and transcribe the exact label for every form ref.
|
|
67
|
+
- Load `tmp/ta6ed6-fields.json` and reconcile each entry against the transcribed PDF text;
|
|
68
|
+
correct the JSON map where the extraction and PDF disagree (PDF wins).
|
|
69
|
+
- Produce an in-memory map `ta6ed6Ref → canonicalTitle` (after applying the §C.5 house style).
|
|
70
|
+
|
|
71
|
+
### 3. Apply titles to `combined.json`
|
|
72
|
+
Walk `combined.json`. For each object carrying a `ta6ed6Ref` that maps to a canonical label:
|
|
73
|
+
- If `canonicalTitle !== base title` → set/replace `ta6ed6Title` with the canonical value, placed
|
|
74
|
+
immediately **after** the `title` property (matching the existing convention:
|
|
75
|
+
`…refs, "title", "ta6ed6Title", "type"…`).
|
|
76
|
+
- If `canonicalTitle === base title` → ensure no redundant `ta6ed6Title` is present.
|
|
77
|
+
- Re-verify each of the existing 51 and correct mismatches.
|
|
78
|
+
Preserve JSON formatting (2-space indent, key order) to keep the diff reviewable. Where a single
|
|
79
|
+
form field maps to two refs (e.g. `documents` + `attachments` sharing a ref, per §C.4), title the
|
|
80
|
+
user-facing question property, not the attachment helper.
|
|
81
|
+
|
|
82
|
+
### 4. Regenerate derived artifacts
|
|
83
|
+
Run `node src/utils/extractOverlay.js` so the new titles propagate into `overlays/ta6ed6.json`
|
|
84
|
+
(and refresh the core schema). Confirm `overlays/ta6ed6.json` `ta6ed6Title` count now matches the
|
|
85
|
+
number of titled fields. Regenerate skeletons too if the repo's release flow does (see recent
|
|
86
|
+
commit "Regenerate overlays and skeletons").
|
|
87
|
+
|
|
88
|
+
## Verification
|
|
89
|
+
|
|
90
|
+
1. **Exactness audit script (read-only):** generate a table of every `ta6ed6Ref` → applied
|
|
91
|
+
`ta6ed6Title` (or inherited base `title`) and diff it against the PDF-derived canonical map.
|
|
92
|
+
Output must show **zero** unexplained mismatches. Save the report alongside the existing
|
|
93
|
+
`tmp/ta6ed6-mapping-report.md` for reviewer sign-off.
|
|
94
|
+
2. **Schema validity:** `npm test` (jest) — all suites green, especially
|
|
95
|
+
`src/tests/v3/ta6ed6.test.js` and `transactionSchema.test.js`.
|
|
96
|
+
3. **Overlay parity:** assert every `ta6ed6Title` in `combined.json` is present in the regenerated
|
|
97
|
+
`overlays/ta6ed6.json` (proves the generation step captured them).
|
|
98
|
+
4. **Spot render check:** optionally load a ta6ed6 transaction via `getTransactionSchema` /
|
|
99
|
+
`getValidator` to confirm titles surface as expected.
|
|
100
|
+
|
|
101
|
+
## Critical files
|
|
102
|
+
|
|
103
|
+
- `src/schemas/v3/combined.json` — primary edit target.
|
|
104
|
+
- `src/schemas/v3/overlays/ta6ed6.json`, `src/schemas/v3/pdtf-transaction.json` — regenerated, not
|
|
105
|
+
hand-edited.
|
|
106
|
+
- `src/utils/extractOverlay.js` — generation script (already lists `ta6ed6`).
|
|
107
|
+
- `tmp/TA6 Form 6th edition.pdf`, `tmp/ta6ed6-fields.json`, `tmp/ta6ed6-mapping-report.md` —
|
|
108
|
+
title sources / reference (git-ignored; not committed).
|
|
109
|
+
- `src/tests/v3/ta6ed6.test.js` — existing test coverage.
|
|
110
|
+
|
|
111
|
+
## Open considerations
|
|
112
|
+
|
|
113
|
+
- The audit in step-4/verification may surface base-schema labels that are *already* exactly right
|
|
114
|
+
for the form (no `ta6ed6Title` needed) — these are correctly left untouched under the
|
|
115
|
+
"only where wording differs" decision.
|
|
116
|
+
- A handful of refs are overlay-only discriminators / aggregations with no printed form label
|
|
117
|
+
(e.g. `areBoundariesUniform`, `isInsured`, `offMainsDrainageSystemType`; see mapping report
|
|
118
|
+
§C.1). These get no `ta6ed6Title`.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Spec: Exact TA7 5th-edition field titles (`ta7ed5Title`)
|
|
2
|
+
|
|
3
|
+
## Context
|
|
4
|
+
|
|
5
|
+
Mirror of the TA6 6th-edition work: add `ta7ed5Title` properties carrying the verbatim
|
|
6
|
+
Law Society *TA7 Leasehold Information Form (5th edition) (2025)* labels, so the form renders
|
|
7
|
+
with compliant labelling. Source verified against `TA7-5th-edition.pdf` (9 pages, 12 sections),
|
|
8
|
+
extracted with `pdftotext`.
|
|
9
|
+
|
|
10
|
+
## Audit (current state on `dev`)
|
|
11
|
+
|
|
12
|
+
- `ta7ed5Ref` markers in `combined.json`: **156**
|
|
13
|
+
- Existing `ta7ed5Title`: **20** (sections 6, 7, 10, 11 mostly done; some retain trailing colons)
|
|
14
|
+
- Candidates (non-empty base title, no `ta7ed5Title`): **109**
|
|
15
|
+
- Empty base title (yesNo/answer components — inherit, no title): 27
|
|
16
|
+
- Overlay `overlays/ta7ed5.json` exists; titles propagate via `extractOverlay.js` as `title`.
|
|
17
|
+
|
|
18
|
+
## House style (same as TA6, confirmed)
|
|
19
|
+
|
|
20
|
+
Strip `(a)`/`(i)` and `If yes,`/`If no,` prefixes; omit trailing colons; **no override when a
|
|
21
|
+
colon is the only difference**; `ta7ed5Title` placed immediately after `title`. Use the form's
|
|
22
|
+
exact wording incl. its curly apostrophes (e.g. landlord’s). Also re-verify the existing 20 and
|
|
23
|
+
strip their trailing colons (4 have them: 8.1.2, 11.2.2, 11.4.2, 11.6.2).
|
|
24
|
+
|
|
25
|
+
## Proposed changes (clear adds — form question/field wording differs from base)
|
|
26
|
+
|
|
27
|
+
| Ref | Property | ta7ed5Title (exact form text, normalised) |
|
|
28
|
+
|-----|----------|-------------------------------------------|
|
|
29
|
+
| 1.1 | address | Address of the property |
|
|
30
|
+
| 1.1.3 | uprn | UPRN (if known) |
|
|
31
|
+
| 4.4.1 | sellerOwnership.yesNo | Does the seller alone, or jointly with others, own the freehold, any headlease or a management company named in the lease? |
|
|
32
|
+
| 4.4.2 | sellerOwnership.attachments | Supply evidence of your ownership or share certificate for membership of the organisation that is responsible for the management of the building. |
|
|
33
|
+
| 5.3 | sellerContributesToServiceCharge | Do you contribute to the cost of maintaining the building? |
|
|
34
|
+
| 5.4 | largeAdditionalExpense | Do you know of any expense (e.g. the cost of redecoration of outside or communal areas that is not usually incurred annually) that is likely to be shown in the service charge accounts within the next three years? |
|
|
35
|
+
| 5.4.2 | details | Please give details |
|
|
36
|
+
| 5.5 | serviceChargeChallenged | Have you challenged the service charge or any expense in the last three years? |
|
|
37
|
+
| 5.6 | problemInServiceChargeLevel | Do you know of any issues in the last three years regarding the level of service charges or with the management? |
|
|
38
|
+
| 7.3 | sellerAwareOfResponse | Are you aware of any response to a notice disclosed in replies to 7.1 and 7.2 above? |
|
|
39
|
+
| 8.1 | changesInTermOfLease | Are you aware of any changes to the terms of the lease? |
|
|
40
|
+
| 9.1 | sellerAwareOfAlterations | Are you aware of any alterations having been made to the property since the lease was originally granted? |
|
|
41
|
+
|
|
42
|
+
(8.2 `landlordConsents`, 8.2.2 `details`, 9.2 `details`, 9.3 `landlordConsentObtained` already match the
|
|
43
|
+
form once colon/case-normalised → no override; confirmed at implementation.)
|
|
44
|
+
|
|
45
|
+
## Decisions to confirm (judgment calls — see questions)
|
|
46
|
+
|
|
47
|
+
**A. Section / sub-group headings** where the schema container name differs from the form heading:
|
|
48
|
+
- 3 `contactDetails` "Contact Details" → **"Contact details"**
|
|
49
|
+
- 3.1.2 `managementCompany` "Management Company" → **"Management company"**
|
|
50
|
+
- 10 `disputes` "Disputes" → **"Complaints"** (form section 10 is "Complaints")
|
|
51
|
+
- 11 `buildingSafetyAct` "Building safety" → **"Building safety for blocks of flats"**
|
|
52
|
+
|
|
53
|
+
**B. Per-contact field labels** (forms label these "Name:" / "Email:"), 5 contacts in §3 + managing
|
|
54
|
+
agent in §4 — ~14 tiny fields:
|
|
55
|
+
- `nameOrOrganisation` "Name or organisation" → **"Name"**
|
|
56
|
+
- `emailAddress` "Email address" → **"Email"**
|
|
57
|
+
- (`address`/`postcode`/`line1` already match or have no distinct form label → skip)
|
|
58
|
+
|
|
59
|
+
**C. "Who collects" questions (§4.2/4.3)** — schema frames these as *who collects*, the form asks a
|
|
60
|
+
yes/no first:
|
|
61
|
+
- 4.2 `collectsbuildingInsurancePremiums` → form 4.2 "Does the managing agent arrange or collect charges for the insurance of the building?"
|
|
62
|
+
- 4.3 `collectsGroundRent` → form 4.3 "If ground rent is payable, is it collected by the managing agent?"
|
|
63
|
+
- (sub-collectors `collector`/`otherCollector` → leave, or map to form's "Other (please provide details)")
|
|
64
|
+
|
|
65
|
+
**D. §2.2 payment-demand fields** — form 2.2 is one question with (a)/(b)/(c) sub-items:
|
|
66
|
+
- Option 1: label the demand-status fields with sub-item names — 2.2a → "Ground rent", 2.2b → "Service charge", 2.2c → "Buildings insurance"
|
|
67
|
+
- Option 2: label each with the full 2.2 question text
|
|
68
|
+
|
|
69
|
+
**E. §5.1/5.2 decoration years** — schema models `externally`/`internally` containers with a `.year`:
|
|
70
|
+
- 5.1 `externally` → "In what year was the outside of the building last decorated?"
|
|
71
|
+
- 5.2 `internally` → "In what year were any internal communal parts last decorated?"
|
|
72
|
+
|
|
73
|
+
## Intentionally NOT titled (no distinct printed TA7 label / structural / overlay-only)
|
|
74
|
+
|
|
75
|
+
Section containers `ownership`, `ownershipsToBeTransferred`, `leaseholdInformation`,
|
|
76
|
+
`ownershipAndManagement`, `requiredDocuments`, `lastDecoratedPeriod`, `contacts`; non-TA7 fields
|
|
77
|
+
`ownershipType`, `isGroundRentPayable`, `typeOfConstruction`; per-line address fields; `13`
|
|
78
|
+
confirmation/declaration block (no numbered form heading); `attachments`/`details` that already
|
|
79
|
+
match the form's "Please supply a copy" / "Please give details".
|
|
80
|
+
|
|
81
|
+
## Out of scope
|
|
82
|
+
|
|
83
|
+
Any TA7 questions lacking a `ta7ed5Ref` (ref-completion is separate, per `fix/ta7ed5-*` work).
|
|
84
|
+
|
|
85
|
+
## Plan after sign-off
|
|
86
|
+
|
|
87
|
+
Replicate the TA6 mechanism: a `scripts/ta7ed5-titles.js` map keyed by `ref::segment`, surgical
|
|
88
|
+
`jsonc-parser` inserts (after `title`), prefix/colon normalisation incl. existing 20, regenerate
|
|
89
|
+
`ta7ed5.json` via `extractOverlay.js`, run `npm test`, audit all titles vs the form. Branch off `dev`.
|