feat(report): add personal report contract and persistence
This commit is contained in:
@@ -0,0 +1,432 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"$id": "https://jyotisha.chat/contracts/personal-report/report-document.v1.schema.json",
|
||||
"title": "ReportDocument v1",
|
||||
"description": "Server-issued personal astrology report document. This contract is enforced identically by the JSON Schema below, frontend/src/lib/personal-report-contract.ts (Zod), and scripts/personal_report_contract.py (stdlib Python validator). Semantics that JSON Schema draft-07 cannot express are enforced by both runtime validators and their tests: (1) charts must contain exactly one D1 chart, chart ids must be unique, and the D1 chart must contain all twelve house numbers 1..12 (enough real houses to render without fabrication); (2) every houseNumber must be unique within its chart; (3) evidence ids (id fields of techniqueAudit, conflicts and calculationEvidence rows) must be globally unique across the whole evidence appendix so evidenceRefs are never ambiguous; (4) provenance.evidenceHash is a deterministic recomputation over the evidence appendix (techniqueAudit, conflicts, calculationEvidence in canonical field order) - it is never trusted as a model self-report; the cryptographic hash is verified by the server runtime (frontend/src/lib/personal-report-contract.server.ts) and by the Python validator (scripts/personal_report_contract.py), and a document whose evidenceHash does not equal the recomputed value is rejected; the isomorphic frontend contract validates structure only and never recomputes the hash; (5) every entry in evidenceRefs must reference an id present in evidenceAppendix.techniqueAudit, evidenceAppendix.conflicts, or evidenceAppendix.calculationEvidence; (6) sections whose claimStatus is blocked must not contain deterministic predictions (e.g. 必然, 必定, 一定会, 肯定会, 绝对会, guaranteed, definitely will); (7) the UTF-8 JSON serialization of the whole document must not exceed 1572864 bytes (1.5 MiB). Fixed reader order: this schema defines the display sequence executiveSummary, thematicNarrative, evidenceAppendix as a UI/type-level presentation contract; JSON object key order is not validated (objects are unordered by definition). Privacy rule: subject/provenance metadata must never repeat full birth date, precise coordinates, or a raw chart payload. Content rule: no HTML/JS/CSS, no executable URLs (javascript:, vbscript:, data:text/html, file:), no internal filesystem paths, no prompt/tool traces or exception stacks, no model secrets or JWTs.",
|
||||
"type": "object",
|
||||
"definitions": {
|
||||
"claimStatus": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"multi_system_consensus",
|
||||
"single_system_inference",
|
||||
"parameter_sensitive",
|
||||
"unclosed_divisional_chart",
|
||||
"user_history_verification_required",
|
||||
"blocked"
|
||||
]
|
||||
},
|
||||
"evidenceId": {
|
||||
"type": "string",
|
||||
"pattern": "^ev-[a-z0-9_-]{1,63}$",
|
||||
"minLength": 4,
|
||||
"maxLength": 67
|
||||
},
|
||||
"sha256Hex": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9a-f]{64}$",
|
||||
"minLength": 64,
|
||||
"maxLength": 64
|
||||
},
|
||||
"iso8601": {
|
||||
"type": "string",
|
||||
"pattern": "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,9})?(Z|[+-]\\d{2}:\\d{2})$",
|
||||
"minLength": 20,
|
||||
"maxLength": 40
|
||||
},
|
||||
"house": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"houseNumber": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 12
|
||||
},
|
||||
"sign": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 40
|
||||
},
|
||||
"occupants": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 40
|
||||
},
|
||||
"maxItems": 12
|
||||
}
|
||||
},
|
||||
"required": ["houseNumber", "sign", "occupants"]
|
||||
},
|
||||
"planet": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 40
|
||||
},
|
||||
"sign": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 40
|
||||
},
|
||||
"longitudeDegrees": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"exclusiveMaximum": 360
|
||||
},
|
||||
"houseNumber": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"maximum": 12
|
||||
},
|
||||
"retrograde": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": ["name", "sign", "longitudeDegrees", "houseNumber", "retrograde"]
|
||||
},
|
||||
"chart": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"enum": ["D1", "D9", "D10"]
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 120
|
||||
},
|
||||
"houses": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/house"
|
||||
},
|
||||
"maxItems": 12
|
||||
},
|
||||
"planets": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/planet"
|
||||
},
|
||||
"maxItems": 12
|
||||
},
|
||||
"claimStatus": {
|
||||
"$ref": "#/definitions/claimStatus"
|
||||
}
|
||||
},
|
||||
"required": ["id", "title", "houses", "claimStatus"]
|
||||
},
|
||||
"thematicSection": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z][a-z0-9_-]{0,63}$",
|
||||
"minLength": 1,
|
||||
"maxLength": 64
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 160
|
||||
},
|
||||
"narrative": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 4000
|
||||
},
|
||||
"actions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 400
|
||||
},
|
||||
"maxItems": 12
|
||||
},
|
||||
"caveats": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 400
|
||||
},
|
||||
"maxItems": 12
|
||||
},
|
||||
"claimStatus": {
|
||||
"$ref": "#/definitions/claimStatus"
|
||||
},
|
||||
"evidenceRefs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/evidenceId"
|
||||
},
|
||||
"maxItems": 24
|
||||
}
|
||||
},
|
||||
"required": ["id", "title", "narrative", "actions", "caveats", "claimStatus", "evidenceRefs"]
|
||||
},
|
||||
"techniqueAuditRow": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"id": {
|
||||
"$ref": "#/definitions/evidenceId"
|
||||
},
|
||||
"techniqueId": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z0-9_.-]{1,80}$",
|
||||
"minLength": 1,
|
||||
"maxLength": 80
|
||||
},
|
||||
"techniqueName": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 160
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": ["verified", "partial", "blocked"]
|
||||
},
|
||||
"used": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"notes": {
|
||||
"type": "string",
|
||||
"maxLength": 500
|
||||
}
|
||||
},
|
||||
"required": ["id", "techniqueId", "techniqueName", "status", "used"]
|
||||
},
|
||||
"conflictRow": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"id": {
|
||||
"$ref": "#/definitions/evidenceId"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 1000
|
||||
},
|
||||
"impact": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 500
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": ["unresolved", "partial", "resolved"]
|
||||
}
|
||||
},
|
||||
"required": ["id", "description", "impact", "status"]
|
||||
},
|
||||
"calculationEvidenceRow": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"id": {
|
||||
"$ref": "#/definitions/evidenceId"
|
||||
},
|
||||
"label": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 160
|
||||
},
|
||||
"value": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 500
|
||||
},
|
||||
"source": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 200
|
||||
}
|
||||
},
|
||||
"required": ["id", "label", "value", "source"]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"schemaVersion": {
|
||||
"type": "string",
|
||||
"const": "report_document.v1"
|
||||
},
|
||||
"reportId": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
|
||||
},
|
||||
"reportType": {
|
||||
"type": "string",
|
||||
"enum": ["personal_full", "personal_thematic"]
|
||||
},
|
||||
"presentationMode": {
|
||||
"type": "string",
|
||||
"enum": ["default", "research"]
|
||||
},
|
||||
"generatedAt": {
|
||||
"$ref": "#/definitions/iso8601"
|
||||
},
|
||||
"subject": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"displayName": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 120
|
||||
},
|
||||
"birthTimeStatus": {
|
||||
"type": "string",
|
||||
"enum": ["reported", "candidate", "accepted", "confirmed"]
|
||||
},
|
||||
"birthPlaceLabel": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 200
|
||||
}
|
||||
},
|
||||
"required": ["displayName", "birthTimeStatus", "birthPlaceLabel"]
|
||||
},
|
||||
"provenance": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"skillSourceCommit": {
|
||||
"type": ["string", "null"],
|
||||
"pattern": "^[0-9a-f]{40}$",
|
||||
"minLength": 40,
|
||||
"maxLength": 40
|
||||
},
|
||||
"skillSnapshotSha256": {
|
||||
"$ref": "#/definitions/sha256Hex"
|
||||
},
|
||||
"calculationHash": {
|
||||
"$ref": "#/definitions/sha256Hex"
|
||||
},
|
||||
"evidenceHash": {
|
||||
"$ref": "#/definitions/sha256Hex"
|
||||
},
|
||||
"reportContractVersion": {
|
||||
"type": "string",
|
||||
"const": "1"
|
||||
}
|
||||
},
|
||||
"required": ["skillSourceCommit", "skillSnapshotSha256", "calculationHash", "evidenceHash", "reportContractVersion"]
|
||||
},
|
||||
"executiveSummary": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"headline": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 200
|
||||
},
|
||||
"summary": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 2000
|
||||
},
|
||||
"priorities": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 200
|
||||
},
|
||||
"maxItems": 8
|
||||
},
|
||||
"overallClaimStatus": {
|
||||
"$ref": "#/definitions/claimStatus"
|
||||
}
|
||||
},
|
||||
"required": ["headline", "summary", "priorities", "overallClaimStatus"]
|
||||
},
|
||||
"charts": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/chart"
|
||||
},
|
||||
"minItems": 1,
|
||||
"maxItems": 3
|
||||
},
|
||||
"thematicNarrative": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/thematicSection"
|
||||
},
|
||||
"maxItems": 12
|
||||
},
|
||||
"evidenceAppendix": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"expandedByDefault": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"techniqueAudit": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/techniqueAuditRow"
|
||||
},
|
||||
"maxItems": 100
|
||||
},
|
||||
"conflicts": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/conflictRow"
|
||||
},
|
||||
"maxItems": 50
|
||||
},
|
||||
"calculationEvidence": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/calculationEvidenceRow"
|
||||
},
|
||||
"maxItems": 100
|
||||
},
|
||||
"blockedTechniques": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 120
|
||||
},
|
||||
"maxItems": 100
|
||||
}
|
||||
},
|
||||
"required": ["expandedByDefault", "techniqueAudit", "conflicts", "calculationEvidence", "blockedTechniques"]
|
||||
},
|
||||
"disclaimer": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 2000
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"schemaVersion",
|
||||
"reportId",
|
||||
"reportType",
|
||||
"presentationMode",
|
||||
"generatedAt",
|
||||
"subject",
|
||||
"provenance",
|
||||
"executiveSummary",
|
||||
"charts",
|
||||
"thematicNarrative",
|
||||
"evidenceAppendix",
|
||||
"disclaimer"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user