fix: require completed photo review before pdf render
This commit is contained in:
@@ -100,12 +100,14 @@ Current behavior:
|
|||||||
- does not require recipient email(s) for the analysis-only run
|
- does not require recipient email(s) for the analysis-only run
|
||||||
- asks for recipient email(s) only when PDF rendering is explicitly requested
|
- asks for recipient email(s) only when PDF rendering is explicitly requested
|
||||||
- does not render/send the PDF from a preliminary helper payload with `decision: pending`
|
- does not render/send the PDF from a preliminary helper payload with `decision: pending`
|
||||||
|
- does not render/send the PDF when `photoReview.status` is not `completed`
|
||||||
- only renders the fixed-template PDF after a decision-grade verdict and fair-value range are actually present
|
- only renders the fixed-template PDF after a decision-grade verdict and fair-value range are actually present
|
||||||
|
|
||||||
Expected agent behavior:
|
Expected agent behavior:
|
||||||
|
|
||||||
- if the user asked for the full assessment, continue beyond the preliminary helper output
|
- if the user asked for the full assessment, continue beyond the preliminary helper output
|
||||||
- fill the remaining gaps with listing facts, comp work, condition interpretation, and valuation logic
|
- fill the remaining gaps with listing facts, comp work, condition interpretation, and valuation logic
|
||||||
|
- require completed subject-unit photo review before treating the report as decision-grade enough for PDF delivery
|
||||||
- only stop early when there is a real blocker, not merely because the helper stopped at a checkpoint
|
- only stop early when there is a real blocker, not merely because the helper stopped at a checkpoint
|
||||||
|
|
||||||
Important limitation:
|
Important limitation:
|
||||||
|
|||||||
@@ -120,12 +120,14 @@ scripts/property-assessor render-report --input "<report-payload-json>" --output
|
|||||||
- only stop and ask for recipient email(s) when the user is explicitly rendering or sending the PDF
|
- only stop and ask for recipient email(s) when the user is explicitly rendering or sending the PDF
|
||||||
- render the PDF only after recipient email(s) are known
|
- render the PDF only after recipient email(s) are known
|
||||||
- do **not** render or send a PDF from the helper's preliminary payload while verdict is still `pending` or fair value is not established
|
- do **not** render or send a PDF from the helper's preliminary payload while verdict is still `pending` or fair value is not established
|
||||||
|
- do **not** render or send a decision-grade PDF while `photoReview.status` is anything other than `completed`
|
||||||
- if comps, valuation, or decision-grade condition interpretation are still incomplete, return the preliminary payload and say that the PDF/send step must wait
|
- if comps, valuation, or decision-grade condition interpretation are still incomplete, return the preliminary payload and say that the PDF/send step must wait
|
||||||
|
|
||||||
Agent follow-through rule:
|
Agent follow-through rule:
|
||||||
- When the user asked for a full property assessment or asked for the PDF/email result, do not stop at the helper output.
|
- When the user asked for a full property assessment or asked for the PDF/email result, do not stop at the helper output.
|
||||||
- After `assess` returns a preliminary payload, continue with the remaining manual/model-driven steps needed to reach a decision-grade report.
|
- After `assess` returns a preliminary payload, continue with the remaining manual/model-driven steps needed to reach a decision-grade report.
|
||||||
- Only after the verdict and fair-value range are established should you render/send the PDF.
|
- Only after the verdict and fair-value range are established should you render/send the PDF.
|
||||||
|
- A verdict and fair-value range are still not enough by themselves; the subject-unit photo review must also be completed before the PDF/send step.
|
||||||
- If the analysis still cannot be completed, explain the first real blocker, not just that the helper was preliminary.
|
- If the analysis still cannot be completed, explain the first real blocker, not just that the helper was preliminary.
|
||||||
- If the user sends `update?`, `and?`, or similar mid-run, answer with status and keep the original assessment going. Do not treat that message as a reset or a cue to stop at the last helper checkpoint.
|
- If the user sends `update?`, `and?`, or similar mid-run, answer with status and keep the original assessment going. Do not treat that message as a reset or a cue to stop at the last helper checkpoint.
|
||||||
- In WhatsApp or similar messaging runs, do **not** start a background `assess` helper and then wait on repeated zero-output polls. That counts as a failed path; abandon it and continue with native search/fetch/browser work.
|
- In WhatsApp or similar messaging runs, do **not** start a background `assess` helper and then wait on repeated zero-output polls. That counts as a failed path; abandon it and continue with native search/fetch/browser work.
|
||||||
|
|||||||
@@ -28,8 +28,10 @@ export interface ReportPayload {
|
|||||||
export function isDecisionGradeReportPayload(payload: ReportPayload): boolean {
|
export function isDecisionGradeReportPayload(payload: ReportPayload): boolean {
|
||||||
const decision = String(payload.verdict?.decision || "").trim().toLowerCase();
|
const decision = String(payload.verdict?.decision || "").trim().toLowerCase();
|
||||||
const fairValueRange = String(payload.verdict?.fairValueRange || "").trim().toLowerCase();
|
const fairValueRange = String(payload.verdict?.fairValueRange || "").trim().toLowerCase();
|
||||||
|
const photoReviewStatus = String(payload.photoReview?.status || "").trim().toLowerCase();
|
||||||
if (!decision || decision === "pending") return false;
|
if (!decision || decision === "pending") return false;
|
||||||
if (!fairValueRange || fairValueRange === "not established") return false;
|
if (!fairValueRange || fairValueRange === "not established") return false;
|
||||||
|
if (photoReviewStatus !== "completed") return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +81,7 @@ export function validateReportPayload(payload: ReportPayload): string[] {
|
|||||||
}
|
}
|
||||||
if (!isDecisionGradeReportPayload(payload)) {
|
if (!isDecisionGradeReportPayload(payload)) {
|
||||||
throw new ReportValidationError(
|
throw new ReportValidationError(
|
||||||
"The report payload is still preliminary. Stop and complete the decision-grade analysis before generating or sending the property assessment PDF."
|
"The report payload is still preliminary. Stop and complete the decision-grade analysis, including subject-unit photo review, before generating or sending the property assessment PDF."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return recipients;
|
return recipients;
|
||||||
|
|||||||
@@ -87,3 +87,23 @@ test("renderReportPdf rejects a preliminary report with pending verdict", async
|
|||||||
/decision-grade|preliminary|pending/i
|
/decision-grade|preliminary|pending/i
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("renderReportPdf rejects a report when subject-unit photo review is not completed", async () => {
|
||||||
|
const outputPath = path.join(os.tmpdir(), `property-assessor-missing-photos-${Date.now()}.pdf`);
|
||||||
|
await assert.rejects(
|
||||||
|
() =>
|
||||||
|
renderReportPdf(
|
||||||
|
{
|
||||||
|
...samplePayload,
|
||||||
|
photoReview: {
|
||||||
|
status: "not completed",
|
||||||
|
source: "accessible listing-photo source not reliably exposed for unit 235",
|
||||||
|
attempts: ["Zillow and HAR photo review did not complete."],
|
||||||
|
summary: "Condition review is incomplete."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
outputPath
|
||||||
|
),
|
||||||
|
/photo review|decision-grade|incomplete/i
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user