diff --git a/backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/README.md b/backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/README.md new file mode 100644 index 000000000..39d704b71 --- /dev/null +++ b/backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/README.md @@ -0,0 +1,35 @@ +# Big Five W2 English Result Content Package + +This directory contains 16 backend-authoritative English content asset groups for the frozen W2 Big Five result/report/share/PDF/history inventory. + +The package is a producer draft for manual review. It is not a runtime registry, selector input, importer payload, CMS write authorization, W9 QA report, production release, or deployment artifact. + +## Frozen source + +- Inventory PR: `EN-PARITY-W2-BIG5-INVENTORY-01` / PR 1866 +- Inventory merge: `897490e4baa31fe197ee50c89f0c3fae6bac408d` +- Inventory package SHA-256: `0f50f4108af14656442ef7d57d410b2e74f8dffced6ed3db372bf848ea051292` +- Inventory source-ledger SHA-256: `facbf57a362a430cdc8b5f6545db4a227e1268d285e2c27beed3c935ea9cf6e2` +- `inventory_row_reconciliation.json` preserves all 118 frozen row IDs: 52 completed public controls, 50 reconciled historical revisions, and 16 result-content rows mapped to exact draft asset IDs and required content/item keys. +- The same reconciliation binds source-aligned semantic anchors for all 30 facet codes; validation fails if any facet loses its pinned glossary meaning. + +## Safety and authority + +- All assets are `pending_manual_review`. +- `runtime_use` is `draft_review_only`. +- Runtime, production, CMS, database, release, indexability, search, and deploy permissions remain false. +- Share copy contains no scores, percentiles, attempt IDs, tokens, or private links. +- PDF and history copy remain private-reader surfaces. +- Analytics reader labels contain no internal metric IDs or personal result data. +- The existing zh-CN registry, runtime selector, importer, schema, and public APIs are unchanged. + +## Validation + +Run: + +```bash +php backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/validate_package.php +cd backend && php artisan test tests/Feature/Content/BigFiveEnglishResultAssetPackageTest.php --no-ansi +``` + +Both checks fail closed on 118-row inventory drift, result-row or required-key mismatch, unit drift, duplicate coverage, malformed JSON/JSONL, CJK leakage, private-field leakage, forbidden claims, permission drift, or SHA mismatch. diff --git a/backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/claim_boundary_report.json b/backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/claim_boundary_report.json new file mode 100644 index 000000000..1ae6f29b7 --- /dev/null +++ b/backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/claim_boundary_report.json @@ -0,0 +1,34 @@ +{ + "schema_version": "fermatmind.big5.en_parity.claim_boundary_report.v1", + "review_type": "codex_skeptical_self_review", + "status": "pending_manual_review", + "human_reviewed": false, + "editorial_approved": false, + "qa_pass": false, + "language_contract": { + "continuous_dimensions": true, + "probabilistic_contextual_language": true, + "high_is_not_better": true, + "low_is_not_worse": true, + "fixed_identity_claims": false + }, + "forbidden_claims": [ + "clinical diagnosis or treatment advice", + "hiring, admission, or screening decision", + "precise career match", + "intelligence, ability, or moral ranking", + "income, success, performance, or relationship guarantee", + "unsupported reliability, validity, norm, or percentile claim" + ], + "privacy_contract": { + "raw_scores": false, + "score_vectors": false, + "percentiles": false, + "attempt_ids": false, + "report_tokens": false, + "private_urls": false, + "personal_result_analytics": false + }, + "findings": [], + "decision": "PASS_FOR_DRAFT_PACKAGE_ONLY" +} diff --git a/backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/content_assets.jsonl b/backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/content_assets.jsonl new file mode 100644 index 000000000..454d865e7 --- /dev/null +++ b/backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/content_assets.jsonl @@ -0,0 +1,16 @@ +{"asset_id":"big5.en.w2.free_preview","unit":"free_preview","locale":"en","status":"pending_manual_review","runtime_use":"draft_review_only","ready_for_runtime":false,"ready_for_production":false,"production_use_allowed":false,"content":{"title":"Your Big Five snapshot","body":"This preview highlights a few tendencies that may be more noticeable in your current responses. It is a starting point for reflection, not a fixed description of who you are.","bullets":["Notice which patterns feel familiar in more than one context.","Keep in mind that habits can shift with roles, demands, and experience.","Use the full report to explore all five dimensions and their facets."]}} +{"asset_id":"big5.en.w2.locked_result","unit":"locked_result","locale":"en","status":"pending_manual_review","runtime_use":"draft_review_only","ready_for_runtime":false,"ready_for_production":false,"production_use_allowed":false,"content":{"title":"More detail is available in the full report","body":"Your saved result can include dimension explanations, facet-level context, and practical reflection prompts. Access describes which report sections are available; it does not imply a more certain or more favorable result.","cta":"View report options"}} +{"asset_id":"big5.en.w2.paid_full_report","unit":"paid_full_report","locale":"en","status":"pending_manual_review","runtime_use":"draft_review_only","ready_for_runtime":false,"ready_for_production":false,"production_use_allowed":false,"content":{"title":"Your full Big Five report","intro":"Read the report as a set of working hypotheses. Each dimension describes a continuum, and the pattern may appear differently across work, relationships, stress, and unfamiliar situations.","section_intro":"Explore the five dimensions, the facets within them, and actions you may want to test. No single score defines your ability, character, or future.","closing":"Keep what is useful, question what does not fit, and revisit the result alongside real examples from your life."}} +{"asset_id":"big5.en.w2.entitlement_levels","unit":"entitlement_levels","locale":"en","status":"pending_manual_review","runtime_use":"draft_review_only","ready_for_runtime":false,"ready_for_production":false,"production_use_allowed":false,"content":{"levels":[{"key":"preview","label":"Preview","description":"A short, private snapshot of selected tendencies."},{"key":"core","label":"Core result","description":"All five dimension ranges with concise explanations."},{"key":"full","label":"Full report","description":"Dimension and facet context, reflection prompts, and practical action ideas."}],"boundary":"Access levels describe available content only. They do not indicate result quality, certainty, or personal potential."}} +{"asset_id":"big5.en.w2.five_dimension_explanations","unit":"five_dimension_explanations","locale":"en","status":"pending_manual_review","runtime_use":"draft_review_only","ready_for_runtime":false,"ready_for_production":false,"production_use_allowed":false,"content":{"dimensions":[{"code":"O","title":"Openness to Experience","body":"This dimension reflects how readily you may explore unfamiliar ideas, perspectives, aesthetics, and ways of doing things. Different contexts can call for curiosity, continuity, or a blend of both."},{"code":"C","title":"Conscientiousness","body":"This dimension reflects how you may organize effort, follow plans, and manage responsibilities. Structure can support reliability, while flexibility can help when priorities change."},{"code":"E","title":"Extraversion","body":"This dimension reflects how you may engage with social stimulation, activity, and outward expression. Quiet and energetic settings can each support different kinds of contribution."},{"code":"A","title":"Agreeableness","body":"This dimension reflects how you may balance cooperation, trust, candor, and personal boundaries. Effective interaction can involve both consideration and clear disagreement."},{"code":"N","title":"Emotional Sensitivity","body":"This dimension reflects how readily you may notice stress, threat, or emotional shifts. Sensitivity can provide early signals, while steadiness can support recovery; neither range determines resilience or worth."}]}} +{"asset_id":"big5.en.w2.facet_subscale_explanations","unit":"facet_subscale_explanations","locale":"en","status":"pending_manual_review","runtime_use":"draft_review_only","ready_for_runtime":false,"ready_for_production":false,"production_use_allowed":false,"content":{"intro":"Facets add detail within each broad dimension. They may vary independently, so a broad range does not require every facet to look the same.","facets":[{"code":"O1","label":"Imagination","description":"Tendency to form inner associations and simulate possibilities beyond the information immediately in front of you."},{"code":"O2","label":"Aesthetic Sensitivity","description":"Sensitivity to form, atmosphere, rhythm, and the texture of details."},{"code":"O3","label":"Emotional Receptivity","description":"Willingness to identify, approach, and express inner feelings, and to use them as information rather than set them aside."},{"code":"O4","label":"Openness to Action","description":"Willingness to use new methods, routes, and experiences to test and revise your approach."},{"code":"O5","label":"Intellectual Exploration","description":"Interest in abstract concepts, system relationships, and explanatory frameworks."},{"code":"O6","label":"Value Flexibility","description":"Readiness to reconsider established rules, traditional positions, and value boundaries."},{"code":"C1","label":"Self-Efficacy","description":"Basic confidence that you can handle tasks, understand requirements, and solve problems."},{"code":"C2","label":"Order","description":"Need for structure, neatness, plans, and predictable arrangements."},{"code":"C3","label":"Dutifulness","description":"Tendency to place commitments, rules, and duties consistently near the front of decisions."},{"code":"C4","label":"Achievement Striving","description":"Drive toward goals, demanding standards, and improving the level of completion."},{"code":"C5","label":"Delayed-Feedback Persistence","description":"Ability to keep making progress when feedback or reward is not immediate."},{"code":"C6","label":"Deliberation","description":"Tendency to assess risks, consequences, and steps before acting."},{"code":"E1","label":"Warmth","description":"Tendency to convey goodwill, closeness, and approachability in one-to-one interactions."},{"code":"E2","label":"Group Participation","description":"Preference for multi-person settings, lively interaction, and social density, including drawing energy from groups."},{"code":"E3","label":"Assertiveness","description":"Readiness to occupy an expressive position, state requests, and influence how a group situation develops."},{"code":"E4","label":"Activity Level","description":"Preferred life speed, task density, and level of physical energy."},{"code":"E5","label":"Excitement Seeking","description":"Need for novel, intense, and high-arousal experiences."},{"code":"E6","label":"Positive Emotion","description":"Frequency of naturally experiencing and expressing lightness, excitement, and enjoyment."},{"code":"A1","label":"Trust","description":"Tendency to begin by treating other people's intentions as trustworthy and offering goodwill before demanding proof."},{"code":"A2","label":"Straightforwardness","description":"Tendency to express ideas directly, transparently, and with relatively little strategy."},{"code":"A3","label":"Altruism","description":"Tendency to include other people's circumstances, needs, and burdens naturally in your considerations."},{"code":"A4","label":"Conflict Mediation","description":"Tendency during disagreement to maintain the relationship, yield when useful, and prevent escalation."},{"code":"A5","label":"Modesty","description":"Willingness to reduce self-prominence and avoid placing yourself at the center."},{"code":"A6","label":"Compassion","description":"Sensitivity to other people's vulnerability, hardship, and emotional pain."},{"code":"N1","label":"Anxiety Sensitivity","description":"Degree of early alertness to risks, errors, and uncertain consequences."},{"code":"N2","label":"Anger Activation","description":"Readiness for internal anger or resistance when obstructed, misunderstood, or when a boundary is violated."},{"code":"N3","label":"Sustained-Stress Depletion","description":"Tendency, under sustained pressure, to become drained, fatigued, or short of the energy needed to keep responding."},{"code":"N4","label":"Social Self-Consciousness","description":"Tendency to feel discomfort, monitor yourself, or withdraw under other people's gaze, evaluation, or social exposure."},{"code":"N5","label":"Overload Exit Urge","description":"Tendency, when overloaded, to want to disconnect, withdraw, or change the situation immediately to reduce pressure."},{"code":"N6","label":"Coping-Limit Vulnerability","description":"Tendency to reach visible coping limits more readily under high pressure, loss of control, or complex demands."}]}} +{"asset_id":"big5.en.w2.score_range_boundary_copy","unit":"score_range_boundary_copy","locale":"en","status":"pending_manual_review","runtime_use":"draft_review_only","ready_for_runtime":false,"ready_for_production":false,"production_use_allowed":false,"content":{"intro":"Ranges describe relative tendencies in this response set, not grades or ability levels.","ranges":[{"key":"lower","label":"Lower range","description":"This tendency may be less prominent or may appear in narrower situations. Lower does not mean worse."},{"key":"middle","label":"Middle range","description":"You may shift between both sides of the dimension depending on context, role, or current demands."},{"key":"higher","label":"Higher range","description":"This tendency may appear more readily across situations. Higher does not mean better."}],"boundary":"Small differences near a range boundary should not be treated as a sharp change in personality."}} +{"asset_id":"big5.en.w2.action_growth_advice","unit":"action_growth_advice","locale":"en","status":"pending_manual_review","runtime_use":"draft_review_only","ready_for_runtime":false,"ready_for_production":false,"production_use_allowed":false,"content":{"title":"Turn the result into small experiments","actions":[{"context":"awareness","prompt":"Choose one tendency and write down two recent situations where it helped and one where it created friction."},{"context":"flexibility","prompt":"Try a low-risk behavior from the other side of the dimension and observe what changes."},{"context":"environment","prompt":"Adjust one cue, routine, or boundary so the behavior you want is easier to repeat."},{"context":"review","prompt":"Revisit the experiment after a week and keep only what was useful in that context."}],"boundary":"These are reflection ideas, not treatment, performance, or career prescriptions."}} +{"asset_id":"big5.en.w2.workplace_relationship_copy","unit":"workplace_relationship_copy","locale":"en","status":"pending_manual_review","runtime_use":"draft_review_only","ready_for_runtime":false,"ready_for_production":false,"production_use_allowed":false,"content":{"workplace":{"title":"At work","body":"Your tendencies may shape how you plan, communicate, respond to change, and manage stimulation. Role expectations and team norms can strengthen or soften the pattern.","prompt":"Which working conditions help you contribute consistently, and which require deliberate adjustment?"},"relationships":{"title":"In relationships","body":"The same tendency may be helpful in one interaction and difficult in another. Consider communication habits, boundaries, repair, and the other person's context rather than ranking one style above another.","prompt":"What helps you stay clear and considerate when your preferences differ?"},"boundary":"This content does not predict job performance, determine employment decisions, rate relationship quality, or guarantee outcomes."}} +{"asset_id":"big5.en.w2.share_public_summary","unit":"share_public_summary","locale":"en","status":"pending_manual_review","runtime_use":"draft_review_only","ready_for_runtime":false,"ready_for_production":false,"production_use_allowed":false,"content":{"title":"A Big Five reflection","summary":"I explored five broad personality dimensions as continua. The result offers context for reflection rather than a fixed identity or prediction.","privacy_note":"This public-safe summary contains no scores, percentiles, private result details, account identifiers, or report links.","allowed_fields":["title","summary","privacy_note"]}} +{"asset_id":"big5.en.w2.pdf_reader_content","unit":"pdf_reader_content","locale":"en","status":"pending_manual_review","runtime_use":"draft_review_only","ready_for_runtime":false,"ready_for_production":false,"production_use_allowed":false,"content":{"cover_title":"Your Big Five report","reader_note":"This private report is for your own reflection. Store or share it with care because it may contain personal result context.","method_note":"The report describes tendencies across five continuous dimensions. It does not diagnose a condition or determine ability, character, suitability, or future outcomes.","footer":"Private reader copy — not intended for public indexing.","public_url_allowed":false,"indexable":false}} +{"asset_id":"big5.en.w2.history_account_reentry","unit":"history_account_reentry","locale":"en","status":"pending_manual_review","runtime_use":"draft_review_only","ready_for_runtime":false,"ready_for_production":false,"production_use_allowed":false,"content":{"history_title":"Your saved Big Five results","history_body":"Return to a saved result to review the context and reflections available to your account. A previous result is a snapshot, not a permanent identity.","reentry_cta":"Open saved result","missing_state":"No saved Big Five result is available for this account.","privacy_note":"Use the signed-in account route. Do not copy private result links into public pages."}} +{"asset_id":"big5.en.w2.result_report_cta","unit":"result_report_cta","locale":"en","status":"pending_manual_review","runtime_use":"draft_review_only","ready_for_runtime":false,"ready_for_production":false,"production_use_allowed":false,"content":{"ctas":[{"key":"explore_dimensions","label":"Explore all five dimensions","support":"See how each continuum may show up in different contexts."},{"key":"review_facets","label":"Review the facets","support":"Look at the narrower tendencies within each dimension."},{"key":"try_reflection","label":"Choose a reflection prompt","support":"Turn one observation into a small, testable action."},{"key":"return_history","label":"Return to saved results","support":"Review your private report from your account."}],"boundary":"CTA copy describes navigation and available content only."}} +{"asset_id":"big5.en.w2.empty_error_expired_access_denied","unit":"empty_error_expired_access_denied","locale":"en","status":"pending_manual_review","runtime_use":"draft_review_only","ready_for_runtime":false,"ready_for_production":false,"production_use_allowed":false,"content":{"states":[{"key":"empty","title":"No result is available","body":"Complete the assessment or open a saved result from your account."},{"key":"error","title":"We could not load this result","body":"Try again later. No result details are shown while the request is incomplete."},{"key":"expired","title":"This access link is no longer valid","body":"Sign in and open the result from your account, or request a new authorized link."},{"key":"access_denied","title":"You do not have access to this result","body":"Use the account that owns the result. We do not reveal result details on this page."}],"fail_closed":true}} +{"asset_id":"big5.en.w2.mobile_desktop_consumption","unit":"mobile_desktop_consumption","locale":"en","status":"pending_manual_review","runtime_use":"draft_review_only","ready_for_runtime":false,"ready_for_production":false,"production_use_allowed":false,"content":{"shared_reader_copy":{"continue_label":"Continue reading","expand_label":"Show details","collapse_label":"Hide details","table_label":"Dimension overview","navigation_label":"Report sections"},"mobile_guidance":"Sections use the same meaning on smaller screens and may be opened one at a time for easier reading.","desktop_guidance":"Sections use the same meaning on larger screens and may appear side by side.","boundary":"Layout changes must not change entitlement, interpretation, or claim meaning."}} +{"asset_id":"big5.en.w2.analytics_reader_labels","unit":"analytics_reader_labels","locale":"en","status":"pending_manual_review","runtime_use":"draft_review_only","ready_for_runtime":false,"ready_for_production":false,"production_use_allowed":false,"content":{"section_label":"Your reflection activity","reader_notice":"Reader-facing activity labels describe how report sections are used in aggregate. They do not display or transmit your trait scores in the label.","events":[{"key":"section_opened","label":"Section opened"},{"key":"reflection_started","label":"Reflection started"},{"key":"reflection_completed","label":"Reflection completed"},{"key":"report_returned","label":"Returned to report"}],"internal_metric_ids":[],"personal_result_fields":[],"boundary":"Reader labels must remain separate from internal metric identifiers and must not carry scores, score vectors, percentiles, attempt identifiers, report tokens, or private links."}} diff --git a/backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/duplicate_report.json b/backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/duplicate_report.json new file mode 100644 index 000000000..9c22e3146 --- /dev/null +++ b/backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/duplicate_report.json @@ -0,0 +1,13 @@ +{ + "schema_version": "fermatmind.big5.en_parity.duplicate_report.v1", + "asset_count": 16, + "unique_asset_id_count": 16, + "unique_unit_count": 16, + "source_inventory_row_count": 118, + "unique_source_inventory_row_id_count": 118, + "duplicate_asset_ids": [], + "duplicate_units": [], + "duplicate_source_inventory_row_ids": [], + "exact_duplicate_content_hashes": [], + "decision": "PASS" +} diff --git a/backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/editorial_self_review.json b/backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/editorial_self_review.json new file mode 100644 index 000000000..f55fa8969 --- /dev/null +++ b/backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/editorial_self_review.json @@ -0,0 +1,30 @@ +{ + "schema_version": "fermatmind.big5.en_parity.editorial_self_review.v1", + "review_type": "codex_skeptical_self_review", + "status": "pending_manual_review", + "human_reviewed": false, + "editorial_approved": false, + "qa_pass": false, + "package_frozen": false, + "checks": { + "natural_independent_english": "PASS_FOR_SELF_REVIEW", + "continuous_dimension_language": "PASS_FOR_SELF_REVIEW", + "probabilistic_contextual_language": "PASS_FOR_SELF_REVIEW", + "high_low_value_neutrality": "PASS_FOR_SELF_REVIEW", + "fixed_identity_language_absent": "PASS_FOR_SELF_REVIEW", + "career_hiring_admission_claims_absent": "PASS_FOR_SELF_REVIEW", + "clinical_claims_absent": "PASS_FOR_SELF_REVIEW", + "unsupported_psychometric_numbers_absent": "PASS_FOR_SELF_REVIEW", + "share_private_fields_absent": "PASS_FOR_SELF_REVIEW", + "private_pdf_history_public_urls_absent": "PASS_FOR_SELF_REVIEW", + "analytics_internal_ids_separated": "PASS_FOR_SELF_REVIEW", + "manual_editorial_review": "PENDING" + }, + "known_limits": [ + "This producer review is not human editorial approval.", + "This producer review is not W9 independent QA.", + "No runtime selector or importer consumes this package.", + "The package has not been imported, published, promoted, or deployed." + ], + "decision": "READY_FOR_MANUAL_REVIEW_NOT_RUNTIME" +} diff --git a/backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/inventory_row_reconciliation.json b/backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/inventory_row_reconciliation.json new file mode 100644 index 000000000..0e8752bbe --- /dev/null +++ b/backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/inventory_row_reconciliation.json @@ -0,0 +1,173 @@ +{ + "schema_version": "fermatmind.big5.en_parity.inventory_row_reconciliation.v1", + "source_inventory_package_sha256": "0f50f4108af14656442ef7d57d410b2e74f8dffced6ed3db372bf848ea051292", + "source_inventory_ledger_sha256": "facbf57a362a430cdc8b5f6545db4a227e1268d285e2c27beed3c935ea9cf6e2", + "source_row_id_set_sha256": "347bd92a3db93d873b44e27c88327e860ee5915e9e7eafb9adb2227e75fef8b7", + "source_row_count": 118, + "cohort_counts": { + "public_profile_control": 52, + "english_historical_revision_verification": 50, + "result_content": 16 + }, + "facet_semantic_anchors": { + "O1": ["inner associations", "simulate possibilities"], + "O2": ["form, atmosphere, rhythm", "texture of details"], + "O3": ["identify, approach, and express inner feelings", "use them as information"], + "O4": ["new methods, routes, and experiences", "test and revise your approach"], + "O5": ["abstract concepts", "system relationships", "explanatory frameworks"], + "O6": ["established rules", "traditional positions", "value boundaries"], + "C1": ["handle tasks", "understand requirements", "solve problems"], + "C2": ["structure, neatness, plans", "predictable arrangements"], + "C3": ["commitments, rules, and duties", "front of decisions"], + "C4": ["goals, demanding standards", "level of completion"], + "C5": ["keep making progress", "feedback or reward is not immediate"], + "C6": ["risks, consequences, and steps", "before acting"], + "E1": ["goodwill, closeness, and approachability", "one-to-one interactions"], + "E2": ["multi-person settings", "social density", "energy from groups"], + "E3": ["occupy an expressive position", "state requests", "influence"], + "E4": ["life speed", "task density", "physical energy"], + "E5": ["novel, intense", "high-arousal experiences"], + "E6": ["frequency", "lightness, excitement, and enjoyment"], + "A1": ["intentions as trustworthy", "offering goodwill"], + "A2": ["directly, transparently", "little strategy"], + "A3": ["circumstances, needs, and burdens", "considerations"], + "A4": ["maintain the relationship", "yield when useful", "prevent escalation"], + "A5": ["reduce self-prominence", "placing yourself at the center"], + "A6": ["vulnerability, hardship", "emotional pain"], + "N1": ["early alertness", "risks, errors", "uncertain consequences"], + "N2": ["internal anger or resistance", "obstructed, misunderstood", "boundary is violated"], + "N3": ["under sustained pressure", "drained, fatigued", "energy needed"], + "N4": ["feel discomfort", "monitor yourself", "gaze, evaluation, or social exposure"], + "N5": ["when overloaded", "disconnect, withdraw", "change the situation immediately"], + "N6": ["visible coping limits", "high pressure, loss of control", "complex demands"] + }, + "public_profile_control_rows": [ + "W2-PUBLIC-domain-agreeableness", + "W2-PUBLIC-domain-conscientiousness", + "W2-PUBLIC-domain-extraversion", + "W2-PUBLIC-domain-neuroticism", + "W2-PUBLIC-domain-openness", + "W2-PUBLIC-facet_detail-achievement-striving", + "W2-PUBLIC-facet_detail-actions", + "W2-PUBLIC-facet_detail-activity", + "W2-PUBLIC-facet_detail-aesthetics", + "W2-PUBLIC-facet_detail-altruism", + "W2-PUBLIC-facet_detail-anger", + "W2-PUBLIC-facet_detail-anxiety", + "W2-PUBLIC-facet_detail-assertiveness", + "W2-PUBLIC-facet_detail-competence", + "W2-PUBLIC-facet_detail-compliance", + "W2-PUBLIC-facet_detail-deliberation", + "W2-PUBLIC-facet_detail-depression", + "W2-PUBLIC-facet_detail-dutifulness", + "W2-PUBLIC-facet_detail-excitement-seeking", + "W2-PUBLIC-facet_detail-feelings", + "W2-PUBLIC-facet_detail-gregariousness", + "W2-PUBLIC-facet_detail-ideas", + "W2-PUBLIC-facet_detail-imagination", + "W2-PUBLIC-facet_detail-impulsiveness", + "W2-PUBLIC-facet_detail-modesty", + "W2-PUBLIC-facet_detail-order", + "W2-PUBLIC-facet_detail-positive-emotions", + "W2-PUBLIC-facet_detail-self-consciousness", + "W2-PUBLIC-facet_detail-self-discipline", + "W2-PUBLIC-facet_detail-straightforwardness", + "W2-PUBLIC-facet_detail-tender-mindedness", + "W2-PUBLIC-facet_detail-trust", + "W2-PUBLIC-facet_detail-values", + "W2-PUBLIC-facet_detail-vulnerability", + "W2-PUBLIC-facet_detail-warmth", + "W2-PUBLIC-facet_hub-facets", + "W2-PUBLIC-hub-big-five", + "W2-PUBLIC-polarity-agreeableness-high", + "W2-PUBLIC-polarity-agreeableness-low", + "W2-PUBLIC-polarity-agreeableness-mid", + "W2-PUBLIC-polarity-conscientiousness-high", + "W2-PUBLIC-polarity-conscientiousness-low", + "W2-PUBLIC-polarity-conscientiousness-mid", + "W2-PUBLIC-polarity-extraversion-high", + "W2-PUBLIC-polarity-extraversion-low", + "W2-PUBLIC-polarity-extraversion-mid", + "W2-PUBLIC-polarity-neuroticism-high", + "W2-PUBLIC-polarity-neuroticism-low", + "W2-PUBLIC-polarity-neuroticism-mid", + "W2-PUBLIC-polarity-openness-high", + "W2-PUBLIC-polarity-openness-low", + "W2-PUBLIC-polarity-openness-mid" + ], + "english_historical_revision_rows": [ + "W2-DRAFT-SLOT-domain-agreeableness", + "W2-DRAFT-SLOT-domain-conscientiousness", + "W2-DRAFT-SLOT-domain-extraversion", + "W2-DRAFT-SLOT-domain-neuroticism", + "W2-DRAFT-SLOT-domain-openness", + "W2-DRAFT-SLOT-polarity-agreeableness-high", + "W2-DRAFT-SLOT-polarity-agreeableness-mid", + "W2-DRAFT-SLOT-polarity-agreeableness-low", + "W2-DRAFT-SLOT-polarity-conscientiousness-high", + "W2-DRAFT-SLOT-polarity-conscientiousness-mid", + "W2-DRAFT-SLOT-polarity-conscientiousness-low", + "W2-DRAFT-SLOT-polarity-extraversion-high", + "W2-DRAFT-SLOT-polarity-extraversion-mid", + "W2-DRAFT-SLOT-polarity-extraversion-low", + "W2-DRAFT-SLOT-polarity-neuroticism-high", + "W2-DRAFT-SLOT-polarity-neuroticism-mid", + "W2-DRAFT-SLOT-polarity-neuroticism-low", + "W2-DRAFT-SLOT-polarity-openness-high", + "W2-DRAFT-SLOT-polarity-openness-mid", + "W2-DRAFT-SLOT-polarity-openness-low", + "W2-DRAFT-SLOT-facet_detail-achievement-striving", + "W2-DRAFT-SLOT-facet_detail-actions", + "W2-DRAFT-SLOT-facet_detail-activity", + "W2-DRAFT-SLOT-facet_detail-aesthetics", + "W2-DRAFT-SLOT-facet_detail-altruism", + "W2-DRAFT-SLOT-facet_detail-anger", + "W2-DRAFT-SLOT-facet_detail-anxiety", + "W2-DRAFT-SLOT-facet_detail-assertiveness", + "W2-DRAFT-SLOT-facet_detail-competence", + "W2-DRAFT-SLOT-facet_detail-compliance", + "W2-DRAFT-SLOT-facet_detail-deliberation", + "W2-DRAFT-SLOT-facet_detail-depression", + "W2-DRAFT-SLOT-facet_detail-dutifulness", + "W2-DRAFT-SLOT-facet_detail-excitement-seeking", + "W2-DRAFT-SLOT-facet_detail-feelings", + "W2-DRAFT-SLOT-facet_detail-gregariousness", + "W2-DRAFT-SLOT-facet_detail-ideas", + "W2-DRAFT-SLOT-facet_detail-imagination", + "W2-DRAFT-SLOT-facet_detail-impulsiveness", + "W2-DRAFT-SLOT-facet_detail-modesty", + "W2-DRAFT-SLOT-facet_detail-order", + "W2-DRAFT-SLOT-facet_detail-positive-emotions", + "W2-DRAFT-SLOT-facet_detail-self-consciousness", + "W2-DRAFT-SLOT-facet_detail-self-discipline", + "W2-DRAFT-SLOT-facet_detail-straightforwardness", + "W2-DRAFT-SLOT-facet_detail-tender-mindedness", + "W2-DRAFT-SLOT-facet_detail-trust", + "W2-DRAFT-SLOT-facet_detail-values", + "W2-DRAFT-SLOT-facet_detail-vulnerability", + "W2-DRAFT-SLOT-facet_detail-warmth" + ], + "result_content_rows": [ + {"row_id":"W2-RESULT-free_preview","unit":"free_preview","stable_asset_identity":"big5.result_content.free_preview","translation_identity":"big5-result:free_preview:zh-CN:en","asset_id":"big5.en.w2.free_preview","required_content_keys":["title","body","bullets"]}, + {"row_id":"W2-RESULT-locked_result","unit":"locked_result","stable_asset_identity":"big5.result_content.locked_result","translation_identity":"big5-result:locked_result:zh-CN:en","asset_id":"big5.en.w2.locked_result","required_content_keys":["title","body","cta"]}, + {"row_id":"W2-RESULT-paid_full_report","unit":"paid_full_report","stable_asset_identity":"big5.result_content.paid_full_report","translation_identity":"big5-result:paid_full_report:zh-CN:en","asset_id":"big5.en.w2.paid_full_report","required_content_keys":["title","intro","section_intro","closing"]}, + {"row_id":"W2-RESULT-entitlement_levels","unit":"entitlement_levels","stable_asset_identity":"big5.result_content.entitlement_levels","translation_identity":"big5-result:entitlement_levels:zh-CN:en","asset_id":"big5.en.w2.entitlement_levels","required_content_keys":["levels","boundary"],"required_item_identities":{"levels.key":["preview","core","full"]}}, + {"row_id":"W2-RESULT-five_dimension_explanations","unit":"five_dimension_explanations","stable_asset_identity":"big5.result_content.five_dimension_explanations","translation_identity":"big5-result:five_dimension_explanations:zh-CN:en","asset_id":"big5.en.w2.five_dimension_explanations","required_content_keys":["dimensions"],"required_item_identities":{"dimensions.code":["O","C","E","A","N"]}}, + {"row_id":"W2-RESULT-facet_subscale_explanations","unit":"facet_subscale_explanations","stable_asset_identity":"big5.result_content.facet_subscale_explanations","translation_identity":"big5-result:facet_subscale_explanations:zh-CN:en","asset_id":"big5.en.w2.facet_subscale_explanations","required_content_keys":["intro","facets"],"required_item_identities":{"facets.code":["O1","O2","O3","O4","O5","O6","C1","C2","C3","C4","C5","C6","E1","E2","E3","E4","E5","E6","A1","A2","A3","A4","A5","A6","N1","N2","N3","N4","N5","N6"]}}, + {"row_id":"W2-RESULT-score_range_boundary_copy","unit":"score_range_boundary_copy","stable_asset_identity":"big5.result_content.score_range_boundary_copy","translation_identity":"big5-result:score_range_boundary_copy:zh-CN:en","asset_id":"big5.en.w2.score_range_boundary_copy","required_content_keys":["intro","ranges","boundary"],"required_item_identities":{"ranges.key":["lower","middle","higher"]}}, + {"row_id":"W2-RESULT-action_growth_advice","unit":"action_growth_advice","stable_asset_identity":"big5.result_content.action_growth_advice","translation_identity":"big5-result:action_growth_advice:zh-CN:en","asset_id":"big5.en.w2.action_growth_advice","required_content_keys":["title","actions","boundary"],"required_item_identities":{"actions.context":["awareness","flexibility","environment","review"]}}, + {"row_id":"W2-RESULT-workplace_relationship_copy","unit":"workplace_relationship_copy","stable_asset_identity":"big5.result_content.workplace_relationship_copy","translation_identity":"big5-result:workplace_relationship_copy:zh-CN:en","asset_id":"big5.en.w2.workplace_relationship_copy","required_content_keys":["workplace","relationships","boundary"]}, + {"row_id":"W2-RESULT-share_public_summary","unit":"share_public_summary","stable_asset_identity":"big5.result_content.share_public_summary","translation_identity":"big5-result:share_public_summary:zh-CN:en","asset_id":"big5.en.w2.share_public_summary","required_content_keys":["title","summary","privacy_note","allowed_fields"],"required_item_identities":{"allowed_fields":["title","summary","privacy_note"]}}, + {"row_id":"W2-RESULT-pdf_reader_content","unit":"pdf_reader_content","stable_asset_identity":"big5.result_content.pdf_reader_content","translation_identity":"big5-result:pdf_reader_content:zh-CN:en","asset_id":"big5.en.w2.pdf_reader_content","required_content_keys":["cover_title","reader_note","method_note","footer","public_url_allowed","indexable"]}, + {"row_id":"W2-RESULT-history_account_reentry","unit":"history_account_reentry","stable_asset_identity":"big5.result_content.history_account_reentry","translation_identity":"big5-result:history_account_reentry:zh-CN:en","asset_id":"big5.en.w2.history_account_reentry","required_content_keys":["history_title","history_body","reentry_cta","missing_state","privacy_note"]}, + {"row_id":"W2-RESULT-result_report_cta","unit":"result_report_cta","stable_asset_identity":"big5.result_content.result_report_cta","translation_identity":"big5-result:result_report_cta:zh-CN:en","asset_id":"big5.en.w2.result_report_cta","required_content_keys":["ctas","boundary"],"required_item_identities":{"ctas.key":["explore_dimensions","review_facets","try_reflection","return_history"]}}, + {"row_id":"W2-RESULT-empty_error_expired_access_denied","unit":"empty_error_expired_access_denied","stable_asset_identity":"big5.result_content.empty_error_expired_access_denied","translation_identity":"big5-result:empty_error_expired_access_denied:zh-CN:en","asset_id":"big5.en.w2.empty_error_expired_access_denied","required_content_keys":["states","fail_closed"],"required_item_identities":{"states.key":["empty","error","expired","access_denied"]}}, + {"row_id":"W2-RESULT-mobile_desktop_consumption","unit":"mobile_desktop_consumption","stable_asset_identity":"big5.result_content.mobile_desktop_consumption","translation_identity":"big5-result:mobile_desktop_consumption:zh-CN:en","asset_id":"big5.en.w2.mobile_desktop_consumption","required_content_keys":["shared_reader_copy","mobile_guidance","desktop_guidance","boundary"]}, + {"row_id":"W2-RESULT-analytics_reader_labels","unit":"analytics_reader_labels","stable_asset_identity":"big5.result_content.analytics_reader_labels","translation_identity":"big5-result:analytics_reader_labels:zh-CN:en","asset_id":"big5.en.w2.analytics_reader_labels","required_content_keys":["section_label","reader_notice","events","internal_metric_ids","personal_result_fields","boundary"],"required_item_identities":{"events.key":["section_opened","reflection_started","reflection_completed","report_returned"],"internal_metric_ids":[],"personal_result_fields":[]}} + ], + "dispositions": { + "public_profile_control": "preserved_completed_control_not_modified", + "english_historical_revision_verification": "preserved_reconciled_history_not_modified", + "result_content": "mapped_to_draft_review_asset" + } +} diff --git a/backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/package_manifest.json b/backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/package_manifest.json new file mode 100644 index 000000000..d0f4b1b1b --- /dev/null +++ b/backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/package_manifest.json @@ -0,0 +1,74 @@ +{ + "schema_version": "fermatmind.big5.en_parity.result_content_package.v1", + "package_id": "BIG5-OCEAN-EN-PARITY-W2-RESULT-CONTENT-V1", + "scale_code": "BIG5_OCEAN", + "locale": "en", + "source_locale": "zh-CN", + "status": "pending_manual_review", + "runtime_use": "draft_review_only", + "ready_for_runtime": false, + "ready_for_production": false, + "production_use_allowed": false, + "human_reviewed": false, + "editorial_approved": false, + "qa_pass": false, + "package_frozen": false, + "source_inventory": { + "pr_id": "EN-PARITY-W2-BIG5-INVENTORY-01", + "pr_number": 1866, + "merge_sha": "897490e4baa31fe197ee50c89f0c3fae6bac408d", + "inventory_package_sha256": "0f50f4108af14656442ef7d57d410b2e74f8dffced6ed3db372bf848ea051292", + "source_ledger_sha256": "facbf57a362a430cdc8b5f6545db4a227e1268d285e2c27beed3c935ea9cf6e2", + "row_id_set_sha256": "347bd92a3db93d873b44e27c88327e860ee5915e9e7eafb9adb2227e75fef8b7", + "row_count": 118, + "cohort_counts": { + "public_profile_control": 52, + "english_historical_revision_verification": 50, + "result_content": 16 + }, + "row_reconciliation_file": "inventory_row_reconciliation.json", + "candidate_status": "inventory_frozen" + }, + "inventory_units": [ + "free_preview", + "locked_result", + "paid_full_report", + "entitlement_levels", + "five_dimension_explanations", + "facet_subscale_explanations", + "score_range_boundary_copy", + "action_growth_advice", + "workplace_relationship_copy", + "share_public_summary", + "pdf_reader_content", + "history_account_reentry", + "result_report_cta", + "empty_error_expired_access_denied", + "mobile_desktop_consumption", + "analytics_reader_labels" + ], + "inventory_unit_count": 16, + "content_asset_count": 16, + "permissions": { + "cms_write": false, + "database_write": false, + "public_release": false, + "indexability_change": false, + "search_submission": false, + "deploy": false + }, + "authority": { + "backend_package_is_draft_authority": true, + "frontend_fallback_is_authority": false, + "runtime_selector_changed": false, + "existing_zh_cn_registry_changed": false, + "importer_changed": false, + "schema_changed": false + }, + "source_baselines": { + "fap_api_commit": "d9f1a5554bb9831a1a9dd87d76b41daf8bcac353", + "zh_cn_registry_git_tree_oid": "0c0c42c20ee1a78164a91093d69c7d616edfa8a7", + "result_page_v2_service_git_tree_oid": "bcf1cdb47da2596e252e4940ccc6cac368218e5f" + }, + "generated_at": "2026-07-31T05:21:44Z" +} diff --git a/backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/sha256_manifest.json b/backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/sha256_manifest.json new file mode 100644 index 000000000..7597da9a4 --- /dev/null +++ b/backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/sha256_manifest.json @@ -0,0 +1,18 @@ +{ + "schema_version": "fermatmind.big5.en_parity.sha256_manifest.v1", + "package_id": "BIG5-OCEAN-EN-PARITY-W2-RESULT-CONTENT-V1", + "files": [ + {"path":"package_manifest.json","sha256":"03e4864904f7ecec26822a65475d3183f8aa3edbaf70e41547fbc54cb6d4f5eb"}, + {"path":"source_ledger.json","sha256":"0e9fe6d9f600e10792ff02c2b88dabfbfc704c7674ac9b77feb1c948fbd5ca99"}, + {"path":"inventory_row_reconciliation.json","sha256":"bc2ff3c48a259b38f6afc3d606261f31e1cfa988348830623853f3b8833fcfe9"}, + {"path":"translation_map.json","sha256":"d2cab97371f0392f647b5c057365ebf7001ecca7cd7b10cd6a172202fe4c965d"}, + {"path":"claim_boundary_report.json","sha256":"13edb5670dc2e13b91e297184fb7787e665378af9c72c0199bb44175566deab8"}, + {"path":"content_assets.jsonl","sha256":"3b06cfbb529a478013d862e968a62a64958e1e8d8eee883153dcfbe0ce33e241"}, + {"path":"surface_coverage.json","sha256":"27e9eebc8184d75a4b6d69a2f9288b17bcf9164ebf301c7a17eb7962cea98b92"}, + {"path":"duplicate_report.json","sha256":"743df9149508a4a3c6afbc6f7259cc4435cd7b7ea215fcdc1a5cc0707d641f0d"}, + {"path":"editorial_self_review.json","sha256":"2d5510233468d6ef2463e324c9a4c28ff76b786aaebf4249b6711326279e25cc"}, + {"path":"README.md","sha256":"df99c21d6ebea9bb6656701fb464e9f8cf7798edd32b94c38b41ae655a66cb89"}, + {"path":"validate_package.php","sha256":"0897c9c2493818cee68055d2506991fa5ebbb0e1a663ebed4c84f86bbb3bc68c"} + ], + "package_sha256": "aea87a8c0545d1be6cb1a32ff981576d62d077a8eab36834f34ec9d41c1bfc81" +} diff --git a/backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/source_ledger.json b/backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/source_ledger.json new file mode 100644 index 000000000..c53923721 --- /dev/null +++ b/backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/source_ledger.json @@ -0,0 +1,35 @@ +{ + "schema_version": "fermatmind.big5.en_parity.result_content_source_ledger.v1", + "package_id": "BIG5-OCEAN-EN-PARITY-W2-RESULT-CONTENT-V1", + "source_inventory_sha256": "0f50f4108af14656442ef7d57d410b2e74f8dffced6ed3db372bf848ea051292", + "source_inventory_ledger_sha256": "facbf57a362a430cdc8b5f6545db4a227e1268d285e2c27beed3c935ea9cf6e2", + "inventory_row_reconciliation": { + "file": "inventory_row_reconciliation.json", + "row_count": 118, + "row_id_set_sha256": "347bd92a3db93d873b44e27c88327e860ee5915e9e7eafb9adb2227e75fef8b7", + "cohort_counts": { + "public_profile_control": 52, + "english_historical_revision_verification": 50, + "result_content": 16 + } + }, + "shared_claim_map": "big5-continuous-contextual-nondiagnostic-v1", + "entries": [ + {"unit":"free_preview","translation_identity":"big5-result:free_preview:zh-CN:en","source_authority":"W2-RESULT-free_preview","asset_id":"big5.en.w2.free_preview"}, + {"unit":"locked_result","translation_identity":"big5-result:locked_result:zh-CN:en","source_authority":"W2-RESULT-locked_result","asset_id":"big5.en.w2.locked_result"}, + {"unit":"paid_full_report","translation_identity":"big5-result:paid_full_report:zh-CN:en","source_authority":"W2-RESULT-paid_full_report","asset_id":"big5.en.w2.paid_full_report"}, + {"unit":"entitlement_levels","translation_identity":"big5-result:entitlement_levels:zh-CN:en","source_authority":"W2-RESULT-entitlement_levels","asset_id":"big5.en.w2.entitlement_levels"}, + {"unit":"five_dimension_explanations","translation_identity":"big5-result:five_dimension_explanations:zh-CN:en","source_authority":"W2-RESULT-five_dimension_explanations","asset_id":"big5.en.w2.five_dimension_explanations"}, + {"unit":"facet_subscale_explanations","translation_identity":"big5-result:facet_subscale_explanations:zh-CN:en","source_authority":"W2-RESULT-facet_subscale_explanations","asset_id":"big5.en.w2.facet_subscale_explanations"}, + {"unit":"score_range_boundary_copy","translation_identity":"big5-result:score_range_boundary_copy:zh-CN:en","source_authority":"W2-RESULT-score_range_boundary_copy","asset_id":"big5.en.w2.score_range_boundary_copy"}, + {"unit":"action_growth_advice","translation_identity":"big5-result:action_growth_advice:zh-CN:en","source_authority":"W2-RESULT-action_growth_advice","asset_id":"big5.en.w2.action_growth_advice"}, + {"unit":"workplace_relationship_copy","translation_identity":"big5-result:workplace_relationship_copy:zh-CN:en","source_authority":"W2-RESULT-workplace_relationship_copy","asset_id":"big5.en.w2.workplace_relationship_copy"}, + {"unit":"share_public_summary","translation_identity":"big5-result:share_public_summary:zh-CN:en","source_authority":"W2-RESULT-share_public_summary","asset_id":"big5.en.w2.share_public_summary"}, + {"unit":"pdf_reader_content","translation_identity":"big5-result:pdf_reader_content:zh-CN:en","source_authority":"W2-RESULT-pdf_reader_content","asset_id":"big5.en.w2.pdf_reader_content"}, + {"unit":"history_account_reentry","translation_identity":"big5-result:history_account_reentry:zh-CN:en","source_authority":"W2-RESULT-history_account_reentry","asset_id":"big5.en.w2.history_account_reentry"}, + {"unit":"result_report_cta","translation_identity":"big5-result:result_report_cta:zh-CN:en","source_authority":"W2-RESULT-result_report_cta","asset_id":"big5.en.w2.result_report_cta"}, + {"unit":"empty_error_expired_access_denied","translation_identity":"big5-result:empty_error_expired_access_denied:zh-CN:en","source_authority":"W2-RESULT-empty_error_expired_access_denied","asset_id":"big5.en.w2.empty_error_expired_access_denied"}, + {"unit":"mobile_desktop_consumption","translation_identity":"big5-result:mobile_desktop_consumption:zh-CN:en","source_authority":"W2-RESULT-mobile_desktop_consumption","asset_id":"big5.en.w2.mobile_desktop_consumption"}, + {"unit":"analytics_reader_labels","translation_identity":"big5-result:analytics_reader_labels:zh-CN:en","source_authority":"W2-RESULT-analytics_reader_labels","asset_id":"big5.en.w2.analytics_reader_labels","classification":"reader_copy_only","internal_metric_ids":[]} + ] +} diff --git a/backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/surface_coverage.json b/backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/surface_coverage.json new file mode 100644 index 000000000..084a66618 --- /dev/null +++ b/backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/surface_coverage.json @@ -0,0 +1,41 @@ +{ + "schema_version": "fermatmind.big5.en_parity.surface_coverage.v1", + "expected_unit_count": 16, + "covered_unit_count": 16, + "source_row_coverage": { + "expected_row_count": 118, + "reconciled_row_count": 118, + "public_profile_control": 52, + "english_historical_revision_verification": 50, + "result_content": 16, + "missing_row_ids": [], + "duplicate_row_ids": [] + }, + "missing_units": [], + "duplicate_units": [], + "coverage": [ + {"unit":"free_preview","asset_id":"big5.en.w2.free_preview","covered":true}, + {"unit":"locked_result","asset_id":"big5.en.w2.locked_result","covered":true}, + {"unit":"paid_full_report","asset_id":"big5.en.w2.paid_full_report","covered":true}, + {"unit":"entitlement_levels","asset_id":"big5.en.w2.entitlement_levels","covered":true}, + {"unit":"five_dimension_explanations","asset_id":"big5.en.w2.five_dimension_explanations","covered":true}, + {"unit":"facet_subscale_explanations","asset_id":"big5.en.w2.facet_subscale_explanations","covered":true}, + {"unit":"score_range_boundary_copy","asset_id":"big5.en.w2.score_range_boundary_copy","covered":true}, + {"unit":"action_growth_advice","asset_id":"big5.en.w2.action_growth_advice","covered":true}, + {"unit":"workplace_relationship_copy","asset_id":"big5.en.w2.workplace_relationship_copy","covered":true}, + {"unit":"share_public_summary","asset_id":"big5.en.w2.share_public_summary","covered":true}, + {"unit":"pdf_reader_content","asset_id":"big5.en.w2.pdf_reader_content","covered":true}, + {"unit":"history_account_reentry","asset_id":"big5.en.w2.history_account_reentry","covered":true}, + {"unit":"result_report_cta","asset_id":"big5.en.w2.result_report_cta","covered":true}, + {"unit":"empty_error_expired_access_denied","asset_id":"big5.en.w2.empty_error_expired_access_denied","covered":true}, + {"unit":"mobile_desktop_consumption","asset_id":"big5.en.w2.mobile_desktop_consumption","covered":true}, + {"unit":"analytics_reader_labels","asset_id":"big5.en.w2.analytics_reader_labels","covered":true} + ], + "state_coverage": { + "empty": true, + "error": true, + "expired": true, + "access_denied": true + }, + "decision": "PASS_FOR_DRAFT_REVIEW" +} diff --git a/backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/translation_map.json b/backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/translation_map.json new file mode 100644 index 000000000..a425415f8 --- /dev/null +++ b/backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/translation_map.json @@ -0,0 +1,30 @@ +{ + "schema_version": "fermatmind.big5.en_parity.result_content_translation_map.v1", + "source_locale": "zh-CN", + "target_locale": "en", + "claim_map": "big5-continuous-contextual-nondiagnostic-v1", + "mappings": [ + {"unit":"free_preview","identity":"big5-result:free_preview:zh-CN:en"}, + {"unit":"locked_result","identity":"big5-result:locked_result:zh-CN:en"}, + {"unit":"paid_full_report","identity":"big5-result:paid_full_report:zh-CN:en"}, + {"unit":"entitlement_levels","identity":"big5-result:entitlement_levels:zh-CN:en"}, + {"unit":"five_dimension_explanations","identity":"big5-result:five_dimension_explanations:zh-CN:en"}, + {"unit":"facet_subscale_explanations","identity":"big5-result:facet_subscale_explanations:zh-CN:en"}, + {"unit":"score_range_boundary_copy","identity":"big5-result:score_range_boundary_copy:zh-CN:en"}, + {"unit":"action_growth_advice","identity":"big5-result:action_growth_advice:zh-CN:en"}, + {"unit":"workplace_relationship_copy","identity":"big5-result:workplace_relationship_copy:zh-CN:en"}, + {"unit":"share_public_summary","identity":"big5-result:share_public_summary:zh-CN:en"}, + {"unit":"pdf_reader_content","identity":"big5-result:pdf_reader_content:zh-CN:en"}, + {"unit":"history_account_reentry","identity":"big5-result:history_account_reentry:zh-CN:en"}, + {"unit":"result_report_cta","identity":"big5-result:result_report_cta:zh-CN:en"}, + {"unit":"empty_error_expired_access_denied","identity":"big5-result:empty_error_expired_access_denied:zh-CN:en"}, + {"unit":"mobile_desktop_consumption","identity":"big5-result:mobile_desktop_consumption:zh-CN:en"}, + {"unit":"analytics_reader_labels","identity":"big5-result:analytics_reader_labels:zh-CN:en"} + ], + "translation_policy": { + "mechanical_translation": false, + "natural_independent_english": true, + "same_claim_boundary": true, + "human_review_required": true + } +} diff --git a/backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/validate_package.php b/backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/validate_package.php new file mode 100644 index 000000000..67e9108ee --- /dev/null +++ b/backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/validate_package.php @@ -0,0 +1,246 @@ + '0f50f4108af14656442ef7d57d410b2e74f8dffced6ed3db372bf848ea051292', + 'source_ledger_sha256' => 'facbf57a362a430cdc8b5f6545db4a227e1268d285e2c27beed3c935ea9cf6e2', + 'row_id_set_sha256' => '347bd92a3db93d873b44e27c88327e860ee5915e9e7eafb9adb2227e75fef8b7', + 'row_count' => 118, + 'cohort_counts' => [ + 'public_profile_control' => 52, + 'english_historical_revision_verification' => 50, + 'result_content' => 16, + ], + 'row_reconciliation_file' => 'inventory_row_reconciliation.json', +]; +foreach ($expectedInventoryEvidence as $field => $expected) { + if (($manifest['source_inventory'][$field] ?? null) !== $expected) { + throw new RuntimeException("Frozen source inventory evidence does not match: {$field}"); + } +} + +$assets = []; +foreach (file($root.'/content_assets.jsonl', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: [] as $line) { + $assets[] = json_decode($line, true, 512, JSON_THROW_ON_ERROR); +} + +$units = array_column($assets, 'unit'); +if (count($assets) !== 16 || count(array_unique($units)) !== 16 || $units !== $expectedUnits) { + throw new RuntimeException('Content assets must cover the 16 frozen units exactly once and in order.'); +} + +$assetIds = array_column($assets, 'asset_id'); +if (count(array_unique($assetIds)) !== 16) { + throw new RuntimeException('Content asset IDs must be unique.'); +} + +$reconciliation = $decode($root.'/inventory_row_reconciliation.json'); +$publicRows = $reconciliation['public_profile_control_rows'] ?? []; +$historicalRows = $reconciliation['english_historical_revision_rows'] ?? []; +$resultRows = $reconciliation['result_content_rows'] ?? []; +$sourceRowIds = [ + ...$publicRows, + ...$historicalRows, + ...array_column($resultRows, 'row_id'), +]; +if ( + ($reconciliation['source_inventory_package_sha256'] ?? null) !== $expectedInventoryEvidence['inventory_package_sha256'] + || ($reconciliation['source_inventory_ledger_sha256'] ?? null) !== $expectedInventoryEvidence['source_ledger_sha256'] + || ($reconciliation['source_row_id_set_sha256'] ?? null) !== $expectedInventoryEvidence['row_id_set_sha256'] + || ($reconciliation['source_row_count'] ?? null) !== 118 + || ($reconciliation['cohort_counts'] ?? null) !== $expectedInventoryEvidence['cohort_counts'] + || count($publicRows) !== 52 + || count($historicalRows) !== 50 + || count($resultRows) !== 16 + || count($sourceRowIds) !== 118 + || count(array_unique($sourceRowIds)) !== 118 + || hash('sha256', implode("\n", $sourceRowIds)."\n") !== $expectedInventoryEvidence['row_id_set_sha256'] +) { + throw new RuntimeException('Frozen 118-row inventory reconciliation does not match.'); +} + +$assetsByUnit = array_column($assets, null, 'unit'); +foreach ($resultRows as $index => $row) { + $unit = $expectedUnits[$index]; + $asset = $assetsByUnit[$unit] ?? null; + if ( + ($row['row_id'] ?? null) !== 'W2-RESULT-'.$unit + || ($row['unit'] ?? null) !== $unit + || ($row['stable_asset_identity'] ?? null) !== 'big5.result_content.'.$unit + || ($row['translation_identity'] ?? null) !== 'big5-result:'.$unit.':zh-CN:en' + || ($row['asset_id'] ?? null) !== 'big5.en.w2.'.$unit + || ($asset['asset_id'] ?? null) !== ($row['asset_id'] ?? null) + ) { + throw new RuntimeException("Frozen result row mapping does not match: {$unit}"); + } + + $content = $asset['content'] ?? []; + foreach ($row['required_content_keys'] ?? [] as $key) { + if (! array_key_exists($key, $content)) { + throw new RuntimeException("Required content key is missing for {$unit}: {$key}"); + } + } + + foreach ($row['required_item_identities'] ?? [] as $path => $expectedIdentities) { + if (str_contains($path, '.')) { + [$collection, $identityKey] = explode('.', $path, 2); + $actualIdentities = array_column($content[$collection] ?? [], $identityKey); + } else { + $actualIdentities = $content[$path] ?? null; + } + if ($actualIdentities !== $expectedIdentities) { + throw new RuntimeException("Required item identities do not match for {$unit}: {$path}"); + } + } +} + +$facetAnchors = $reconciliation['facet_semantic_anchors'] ?? []; +$facetsByCode = array_column( + $assetsByUnit['facet_subscale_explanations']['content']['facets'] ?? [], + null, + 'code' +); +if (array_keys($facetAnchors) !== array_keys($facetsByCode) || count($facetAnchors) !== 30) { + throw new RuntimeException('Facet semantic anchor cohort does not match all 30 facets.'); +} +foreach ($facetAnchors as $code => $anchors) { + $semanticText = strtolower( + (string) ($facetsByCode[$code]['label'] ?? '').' '. + (string) ($facetsByCode[$code]['description'] ?? '') + ); + foreach ($anchors as $anchor) { + if (! str_contains($semanticText, strtolower((string) $anchor))) { + throw new RuntimeException("Facet source semantic anchor is missing for {$code}: {$anchor}"); + } + } +} + +$encodedAssets = json_encode($assets, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); +if (preg_match('/[\x{3400}-\x{9FFF}\x{F900}-\x{FAFF}]/u', $encodedAssets) === 1) { + throw new RuntimeException('CJK leakage detected.'); +} + +$forbiddenText = [ + 'guaranteed career', + 'perfect career', + 'hiring fit', + 'admission decision', + 'clinical diagnosis', + 'treatment plan', + 'intelligence score', + 'moral ranking', + 'salary guarantee', + 'success guarantee', + 'relationship guarantee', +]; +foreach ($forbiddenText as $term) { + if (str_contains(strtolower($encodedAssets), $term)) { + throw new RuntimeException("Forbidden claim detected: {$term}"); + } +} + +foreach ($assets as $asset) { + foreach ([ + 'status' => 'pending_manual_review', + 'runtime_use' => 'draft_review_only', + 'ready_for_runtime' => false, + 'ready_for_production' => false, + 'production_use_allowed' => false, + ] as $field => $expected) { + if (($asset[$field] ?? null) !== $expected) { + throw new RuntimeException("Invalid {$field} for {$asset['asset_id']}"); + } + } +} + +$privatePatterns = [ + '/"raw_score"\s*:\s*(?!false|null)/i', + '/"score_vector"\s*:\s*(?!false|null)/i', + '/"percentile"\s*:\s*(?!false|null)/i', + '/"attempt_id"\s*:\s*(?!false|null)/i', + '/"report_token"\s*:\s*(?!false|null)/i', + '/https?:\/\/\S+/i', +]; +foreach ($privatePatterns as $pattern) { + if (preg_match($pattern, $encodedAssets) === 1) { + throw new RuntimeException("Private-field or public-URL leakage detected: {$pattern}"); + } +} + +$permissions = $manifest['permissions'] ?? []; +foreach (['cms_write', 'database_write', 'public_release', 'indexability_change', 'search_submission', 'deploy'] as $permission) { + if (($permissions[$permission] ?? null) !== false) { + throw new RuntimeException("Permission must remain false: {$permission}"); + } +} + +$shaManifest = $decode($root.'/sha256_manifest.json'); +$manifestedPackageFiles = array_column($shaManifest['files'] ?? [], 'path'); +$expectedPackageFiles = [...$manifestedPackageFiles, 'sha256_manifest.json']; +$actualPackageFiles = array_values(array_filter( + scandir($root) ?: [], + static fn (string $file): bool => $file !== '.' && $file !== '..' && is_file($root.'/'.$file) +)); +sort($expectedPackageFiles); +sort($actualPackageFiles); +if ($actualPackageFiles !== $expectedPackageFiles) { + throw new RuntimeException('Package directory contains missing or unmanifested files.'); +} + +$canonical = []; +foreach ($shaManifest['files'] ?? [] as $file) { + $path = $root.'/'.$file['path']; + $actual = hash_file('sha256', $path); + if (! hash_equals((string) $file['sha256'], $actual)) { + throw new RuntimeException("SHA mismatch for {$file['path']}"); + } + $canonical[] = $file['path'].':'.$actual; +} + +$packageSha = hash('sha256', implode("\n", $canonical)); +if (! hash_equals((string) ($shaManifest['package_sha256'] ?? ''), $packageSha)) { + throw new RuntimeException('Package SHA mismatch.'); +} + +echo json_encode([ + 'ok' => true, + 'unit_count' => count($units), + 'asset_count' => count($assets), + 'package_sha256' => $packageSha, + 'runtime_use' => 'draft_review_only', +], JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR).PHP_EOL; diff --git a/backend/docs/seo/en-parity-w2-big-five-result-assets.md b/backend/docs/seo/en-parity-w2-big-five-result-assets.md new file mode 100644 index 000000000..f4f9261b5 --- /dev/null +++ b/backend/docs/seo/en-parity-w2-big-five-result-assets.md @@ -0,0 +1,44 @@ +# Big Five W2 English Result Assets + +## Outcome + +`EN-PARITY-W2-BIG5-RESULT-ASSETS-01` produces one backend draft-review-only package covering all 16 frozen result/report/share/PDF/history inventory units from fap-web PR 1866. + +The package contains natural English reader copy for previews, locked and full reports, entitlements, five dimensions, 30 facets, score-range boundaries, practical reflection, workplace and relationship context, public-safe sharing, private PDF/history use, calls to action, fail-closed states, cross-device reading, and reader-facing analytics labels. + +It also reconciles every row in the immutable 118-row source inventory: 52 completed public-profile controls and 50 historical revision rows remain preserved and unmodified, while all 16 result-content rows map to exact draft asset IDs, content keys, and nested item identities. Source-aligned semantic anchors cover all 30 facet codes. + +## Exact lineage + +- Inventory merge SHA: `897490e4baa31fe197ee50c89f0c3fae6bac408d` +- Inventory package SHA-256: `0f50f4108af14656442ef7d57d410b2e74f8dffced6ed3db372bf848ea051292` +- Result content package SHA-256: `aea87a8c0545d1be6cb1a32ff981576d62d077a8eab36834f34ec9d41c1bfc81` + +## Boundaries + +- Every asset is `pending_manual_review`. +- `runtime_use` is `draft_review_only`. +- No asset is ready for runtime or production. +- No CMS, database, publish, indexability, search, or deploy permission is granted. +- Share copy contains no scores, score vectors, percentiles, private URLs, attempt IDs, or report tokens. +- PDF and history copy remain private-reader content and expose no public URL. +- Analytics reader labels contain no internal metric IDs or personal result fields. +- High and low ranges are value-neutral; dimensions are continuous and contextual. +- The copy does not diagnose, rank ability or character, make hiring/admission decisions, prescribe careers, or guarantee outcomes. + +## Review state + +Codex performed a skeptical producer self-review. That review is not human editorial approval and is not W9 independent QA. The package is not frozen, imported, promoted, published, or deployed. + +## Repository rule impact + +This PR adds a draft-only backend content package and its validation evidence. It does not change the existing zh-CN registry, runtime selector, importer, schema, API routes, CMS authority, or frontend no-fallback boundary. + +## Validation + +```bash +php backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/validate_package.php +php -l backend/tests/Feature/Content/BigFiveEnglishResultAssetPackageTest.php +cd backend && php artisan test tests/Feature/Content/BigFiveEnglishResultAssetPackageTest.php --no-ansi +cd backend && composer test:content +``` diff --git a/backend/docs/seo/generated/en-parity-w2-big-five-result-assets.v1.json b/backend/docs/seo/generated/en-parity-w2-big-five-result-assets.v1.json new file mode 100644 index 000000000..2b7be0343 --- /dev/null +++ b/backend/docs/seo/generated/en-parity-w2-big-five-result-assets.v1.json @@ -0,0 +1,68 @@ +{ + "schema_version": "en-parity-w2-big-five-result-assets.v1", + "pr_id": "EN-PARITY-W2-BIG5-RESULT-ASSETS-01", + "title": "Big Five W2 English result assets", + "source_inventory": { + "pr_number": 1866, + "merge_sha": "897490e4baa31fe197ee50c89f0c3fae6bac408d", + "package_sha256": "0f50f4108af14656442ef7d57d410b2e74f8dffced6ed3db372bf848ea051292", + "source_ledger_sha256": "facbf57a362a430cdc8b5f6545db4a227e1268d285e2c27beed3c935ea9cf6e2", + "row_id_set_sha256": "347bd92a3db93d873b44e27c88327e860ee5915e9e7eafb9adb2227e75fef8b7", + "row_count": 118, + "cohort_counts": { + "public_profile_control": 52, + "english_historical_revision_verification": 50, + "result_content": 16 + }, + "status": "inventory_frozen" + }, + "output_package": { + "path": "backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1", + "package_sha256": "aea87a8c0545d1be6cb1a32ff981576d62d077a8eab36834f34ec9d41c1bfc81", + "inventory_unit_count": 16, + "covered_unit_count": 16, + "asset_group_count": 16, + "locale": "en", + "status": "pending_manual_review", + "runtime_use": "draft_review_only" + }, + "validation": { + "exact_units": "PASS", + "exact_118_source_rows": "PASS", + "result_row_required_keys": "PASS", + "all_30_facet_semantic_anchors": "PASS", + "unique_traceability": "PASS", + "english_complete": "PASS", + "cjk_leakage": 0, + "private_field_leakage": 0, + "forbidden_claim_findings": 0, + "fixed_type_findings": 0, + "share_safe": "PASS", + "entitlement_separated": "PASS", + "empty_error_expired_access_denied": "PASS", + "analytics_internal_ids_separated": "PASS", + "deterministic_sha": "PASS" + }, + "permissions": { + "ready_for_runtime": false, + "ready_for_production": false, + "production_use_allowed": false, + "cms_write": false, + "database_write": false, + "public_release": false, + "indexability_change": false, + "search_submission": false, + "deploy": false + }, + "review": { + "codex_skeptical_self_review": true, + "human_reviewed": false, + "editorial_approved": false, + "qa_pass": false, + "package_frozen": false, + "w9_independent_qa": "not_performed" + }, + "repository_rule_impact": "Draft-only backend content package. Existing backend authority, zh-CN registry, runtime selector, importer, schema, public API, and frontend no-fallback rules are unchanged.", + "decision": "READY_FOR_MANUAL_REVIEW_NOT_RUNTIME", + "generated_at": "2026-07-31T05:21:44Z" +} diff --git a/backend/tests/Feature/Content/BigFiveEnglishResultAssetPackageTest.php b/backend/tests/Feature/Content/BigFiveEnglishResultAssetPackageTest.php new file mode 100644 index 000000000..9f5ecba70 --- /dev/null +++ b/backend/tests/Feature/Content/BigFiveEnglishResultAssetPackageTest.php @@ -0,0 +1,399 @@ +loadJson('package_manifest.json'); + $assets = $this->assets(); + $ledger = $this->loadJson('source_ledger.json'); + $translationMap = $this->loadJson('translation_map.json'); + $coverage = $this->loadJson('surface_coverage.json'); + + $this->assertSame(self::UNITS, $manifest['inventory_units']); + $this->assertSame(16, $manifest['inventory_unit_count']); + $this->assertCount(16, $assets); + $this->assertSame(self::UNITS, array_column($assets, 'unit')); + $this->assertCount(16, array_unique(array_column($assets, 'unit'))); + $this->assertCount(16, array_unique(array_column($assets, 'asset_id'))); + $this->assertSame(self::UNITS, array_column($ledger['entries'], 'unit')); + $this->assertSame(self::UNITS, array_column($translationMap['mappings'], 'unit')); + $this->assertSame(16, $coverage['covered_unit_count']); + $this->assertSame([], $coverage['missing_units']); + $this->assertSame([], $coverage['duplicate_units']); + } + + #[Test] + public function it_reconciles_all_118_frozen_source_rows_and_required_result_keys(): void + { + $manifest = $this->loadJson('package_manifest.json'); + $ledger = $this->loadJson('source_ledger.json'); + $reconciliation = $this->loadJson('inventory_row_reconciliation.json'); + $assetsByUnit = array_column($this->assets(), null, 'unit'); + $expectedCounts = [ + 'public_profile_control' => 52, + 'english_historical_revision_verification' => 50, + 'result_content' => 16, + ]; + + $this->assertSame(118, $manifest['source_inventory']['row_count']); + $this->assertSame($expectedCounts, $manifest['source_inventory']['cohort_counts']); + $this->assertSame('inventory_row_reconciliation.json', $manifest['source_inventory']['row_reconciliation_file']); + $this->assertSame(118, $ledger['inventory_row_reconciliation']['row_count']); + $this->assertSame($expectedCounts, $ledger['inventory_row_reconciliation']['cohort_counts']); + $this->assertSame(52, count($reconciliation['public_profile_control_rows'])); + $this->assertSame(50, count($reconciliation['english_historical_revision_rows'])); + $this->assertSame(16, count($reconciliation['result_content_rows'])); + + $sourceRowIds = [ + ...$reconciliation['public_profile_control_rows'], + ...$reconciliation['english_historical_revision_rows'], + ...array_column($reconciliation['result_content_rows'], 'row_id'), + ]; + $this->assertCount(118, $sourceRowIds); + $this->assertCount(118, array_unique($sourceRowIds)); + $this->assertSame( + '347bd92a3db93d873b44e27c88327e860ee5915e9e7eafb9adb2227e75fef8b7', + hash('sha256', implode("\n", $sourceRowIds)."\n") + ); + + foreach ($reconciliation['result_content_rows'] as $index => $row) { + $unit = self::UNITS[$index]; + $asset = $assetsByUnit[$unit]; + + $this->assertSame('W2-RESULT-'.$unit, $row['row_id']); + $this->assertSame($unit, $row['unit']); + $this->assertSame('big5.result_content.'.$unit, $row['stable_asset_identity']); + $this->assertSame('big5-result:'.$unit.':zh-CN:en', $row['translation_identity']); + $this->assertSame($asset['asset_id'], $row['asset_id']); + + foreach ($row['required_content_keys'] as $key) { + $this->assertArrayHasKey($key, $asset['content'], "{$unit}.{$key}"); + } + + foreach ($row['required_item_identities'] ?? [] as $path => $expectedIdentities) { + if (str_contains($path, '.')) { + [$collection, $identityKey] = explode('.', $path, 2); + $actualIdentities = array_column($asset['content'][$collection], $identityKey); + } else { + $actualIdentities = $asset['content'][$path]; + } + $this->assertSame($expectedIdentities, $actualIdentities, "{$unit}.{$path}"); + } + } + + $facetsByCode = array_column( + $assetsByUnit['facet_subscale_explanations']['content']['facets'], + null, + 'code' + ); + $this->assertCount(30, $reconciliation['facet_semantic_anchors']); + $this->assertSame(array_keys($facetsByCode), array_keys($reconciliation['facet_semantic_anchors'])); + foreach ($reconciliation['facet_semantic_anchors'] as $code => $anchors) { + $semanticText = strtolower($facetsByCode[$code]['label'].' '.$facetsByCode[$code]['description']); + foreach ($anchors as $anchor) { + $this->assertStringContainsString(strtolower($anchor), $semanticText, "{$code}: {$anchor}"); + } + } + } + + #[Test] + public function it_keeps_every_asset_draft_only_and_every_permission_false(): void + { + $manifest = $this->loadJson('package_manifest.json'); + + foreach ($this->assets() as $asset) { + $this->assertSame('en', $asset['locale']); + $this->assertSame('pending_manual_review', $asset['status']); + $this->assertSame('draft_review_only', $asset['runtime_use']); + $this->assertFalse($asset['ready_for_runtime']); + $this->assertFalse($asset['ready_for_production']); + $this->assertFalse($asset['production_use_allowed']); + } + + $this->assertSame('pending_manual_review', $manifest['status']); + $this->assertSame('draft_review_only', $manifest['runtime_use']); + $this->assertFalse($manifest['ready_for_runtime']); + $this->assertFalse($manifest['ready_for_production']); + $this->assertFalse($manifest['production_use_allowed']); + $this->assertFalse($manifest['human_reviewed']); + $this->assertFalse($manifest['editorial_approved']); + $this->assertFalse($manifest['qa_pass']); + $this->assertFalse($manifest['package_frozen']); + foreach ($manifest['permissions'] as $allowed) { + $this->assertFalse($allowed); + } + } + + #[Test] + public function it_blocks_cjk_private_fields_public_urls_and_forbidden_claims(): void + { + $encoded = json_encode($this->assets(), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); + + $this->assertDoesNotMatchRegularExpression('/[\x{3400}-\x{9FFF}\x{F900}-\x{FAFF}]/u', $encoded); + $this->assertDoesNotMatchRegularExpression('/https?:\/\/\S+/i', $encoded); + + foreach ([ + 'guaranteed career', + 'perfect career', + 'hiring fit', + 'admission decision', + 'clinical diagnosis', + 'treatment plan', + 'intelligence score', + 'moral ranking', + 'salary guarantee', + 'success guarantee', + 'relationship guarantee', + ] as $claim) { + $this->assertStringNotContainsString($claim, strtolower($encoded)); + } + + foreach (['raw_score', 'score_vector', 'percentile', 'attempt_id', 'report_token', 'private_url'] as $privateField) { + $this->assertStringNotContainsString('"'.$privateField.'":', $encoded); + } + } + + #[Test] + public function it_enforces_share_private_reader_entitlement_and_failure_boundaries(): void + { + $byUnit = []; + foreach ($this->assets() as $asset) { + $byUnit[$asset['unit']] = $asset['content']; + } + + $this->assertSame( + ['title', 'summary', 'privacy_note'], + $byUnit['share_public_summary']['allowed_fields'] + ); + $this->assertFalse($byUnit['pdf_reader_content']['public_url_allowed']); + $this->assertFalse($byUnit['pdf_reader_content']['indexable']); + $this->assertCount(3, $byUnit['entitlement_levels']['levels']); + $this->assertSame( + ['empty', 'error', 'expired', 'access_denied'], + array_column($byUnit['empty_error_expired_access_denied']['states'], 'key') + ); + $this->assertTrue($byUnit['empty_error_expired_access_denied']['fail_closed']); + $this->assertSame([], $byUnit['analytics_reader_labels']['internal_metric_ids']); + $this->assertSame([], $byUnit['analytics_reader_labels']['personal_result_fields']); + $this->assertCount(5, $byUnit['five_dimension_explanations']['dimensions']); + $this->assertCount(30, $byUnit['facet_subscale_explanations']['facets']); + $o3 = array_values(array_filter( + $byUnit['facet_subscale_explanations']['facets'], + static fn (array $facet): bool => $facet['code'] === 'O3' + ))[0]; + $this->assertSame('Emotional Receptivity', $o3['label']); + $this->assertStringContainsString('identify, approach, and express inner feelings', $o3['description']); + $this->assertStringContainsString('use them as information', $o3['description']); + $c5 = array_values(array_filter( + $byUnit['facet_subscale_explanations']['facets'], + static fn (array $facet): bool => $facet['code'] === 'C5' + ))[0]; + $this->assertSame('Delayed-Feedback Persistence', $c5['label']); + $this->assertStringContainsString('keep making progress', $c5['description']); + $this->assertStringContainsString('feedback or reward is not immediate', $c5['description']); + $e2 = array_values(array_filter( + $byUnit['facet_subscale_explanations']['facets'], + static fn (array $facet): bool => $facet['code'] === 'E2' + ))[0]; + $this->assertSame('Group Participation', $e2['label']); + $this->assertStringContainsString('multi-person settings', $e2['description']); + $this->assertStringContainsString('social density', $e2['description']); + $this->assertStringContainsString('energy from groups', $e2['description']); + $a4 = array_values(array_filter( + $byUnit['facet_subscale_explanations']['facets'], + static fn (array $facet): bool => $facet['code'] === 'A4' + ))[0]; + $this->assertSame('Conflict Mediation', $a4['label']); + $this->assertStringContainsString('maintain the relationship', $a4['description']); + $this->assertStringContainsString('yield when useful', $a4['description']); + $this->assertStringContainsString('prevent escalation', $a4['description']); + $n2 = array_values(array_filter( + $byUnit['facet_subscale_explanations']['facets'], + static fn (array $facet): bool => $facet['code'] === 'N2' + ))[0]; + $this->assertSame('Anger Activation', $n2['label']); + $this->assertStringContainsString('internal anger or resistance', $n2['description']); + $this->assertStringContainsString('obstructed, misunderstood', $n2['description']); + $this->assertStringContainsString('boundary is violated', $n2['description']); + $n3 = array_values(array_filter( + $byUnit['facet_subscale_explanations']['facets'], + static fn (array $facet): bool => $facet['code'] === 'N3' + ))[0]; + $this->assertSame('Sustained-Stress Depletion', $n3['label']); + $this->assertStringContainsString('under sustained pressure', $n3['description']); + $this->assertStringContainsString('drained, fatigued', $n3['description']); + $this->assertStringContainsString('energy needed to keep responding', $n3['description']); + $n4 = array_values(array_filter( + $byUnit['facet_subscale_explanations']['facets'], + static fn (array $facet): bool => $facet['code'] === 'N4' + ))[0]; + $this->assertSame('Social Self-Consciousness', $n4['label']); + $this->assertStringContainsString('feel discomfort', $n4['description']); + $this->assertStringContainsString('monitor yourself', $n4['description']); + $this->assertStringContainsString('gaze, evaluation, or social exposure', $n4['description']); + $n5 = array_values(array_filter( + $byUnit['facet_subscale_explanations']['facets'], + static fn (array $facet): bool => $facet['code'] === 'N5' + ))[0]; + $this->assertSame('Overload Exit Urge', $n5['label']); + $this->assertStringContainsString('when overloaded', $n5['description']); + $this->assertStringContainsString('disconnect, withdraw, or change the situation immediately', $n5['description']); + $n6 = array_values(array_filter( + $byUnit['facet_subscale_explanations']['facets'], + static fn (array $facet): bool => $facet['code'] === 'N6' + ))[0]; + $this->assertSame('Coping-Limit Vulnerability', $n6['label']); + $this->assertStringContainsString('visible coping limits', $n6['description']); + $this->assertStringContainsString('high pressure, loss of control, or complex demands', $n6['description']); + } + + #[Test] + public function it_has_a_deterministic_byte_exact_sha_manifest(): void + { + $shaManifest = $this->loadJson('sha256_manifest.json'); + $expectedPackageFiles = [ + ...array_column($shaManifest['files'], 'path'), + 'sha256_manifest.json', + ]; + $actualPackageFiles = array_map( + 'basename', + glob($this->path('*')) ?: [] + ); + sort($expectedPackageFiles); + sort($actualPackageFiles); + $this->assertSame($expectedPackageFiles, $actualPackageFiles); + + $canonical = []; + + foreach ($shaManifest['files'] as $file) { + $actual = hash_file('sha256', $this->path($file['path'])); + $this->assertSame($file['sha256'], $actual, $file['path']); + $canonical[] = $file['path'].':'.$actual; + } + + $this->assertSame( + $shaManifest['package_sha256'], + hash('sha256', implode("\n", $canonical)) + ); + $this->assertSame( + 'aea87a8c0545d1be6cb1a32ff981576d62d077a8eab36834f34ec9d41c1bfc81', + $shaManifest['package_sha256'] + ); + } + + #[Test] + public function it_pins_unchanged_zh_registry_and_runtime_selector_baselines(): void + { + $manifest = $this->loadJson('package_manifest.json'); + $commit = $manifest['source_baselines']['fap_api_commit']; + + $this->assertSame( + '0c0c42c20ee1a78164a91093d69c7d616edfa8a7', + $manifest['source_baselines']['zh_cn_registry_git_tree_oid'] + ); + $this->assertSame( + 'bcf1cdb47da2596e252e4940ccc6cac368218e5f', + $manifest['source_baselines']['result_page_v2_service_git_tree_oid'] + ); + if ($this->gitCommitIsAvailable($commit)) { + $this->assertSame( + $manifest['source_baselines']['zh_cn_registry_git_tree_oid'], + $this->gitTreeOid($commit, 'backend/content_packs/BIG5_OCEAN/v2/registry') + ); + $this->assertSame( + $manifest['source_baselines']['result_page_v2_service_git_tree_oid'], + $this->gitTreeOid($commit, 'backend/app/Services/BigFive/ResultPageV2') + ); + } + $this->assertFalse($manifest['authority']['existing_zh_cn_registry_changed']); + $this->assertFalse($manifest['authority']['runtime_selector_changed']); + $this->assertFalse($manifest['authority']['importer_changed']); + $this->assertFalse($manifest['authority']['schema_changed']); + } + + /** + * @return array + */ + private function loadJson(string $file): array + { + return json_decode( + (string) file_get_contents($this->path($file)), + true, + 512, + JSON_THROW_ON_ERROR + ); + } + + /** + * @return list> + */ + private function assets(): array + { + $assets = []; + foreach (file($this->path('content_assets.jsonl'), FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: [] as $line) { + $asset = json_decode($line, true, 512, JSON_THROW_ON_ERROR); + $this->assertIsArray($asset); + $assets[] = $asset; + } + + return $assets; + } + + private function path(string $file): string + { + return base_path(self::PACKAGE.'/'.$file); + } + + private function gitCommitIsAvailable(string $commit): bool + { + exec( + 'git -C '.escapeshellarg(dirname(base_path())). + ' cat-file -e '.escapeshellarg($commit.'^{commit}').' 2>/dev/null', + $output, + $status + ); + + return $status === 0; + } + + private function gitTreeOid(string $commit, string $path): string + { + $command = 'git -C '.escapeshellarg(dirname(base_path())). + ' rev-parse '.escapeshellarg($commit.':'.$path); + $value = shell_exec($command); + + $this->assertNotNull($value); + + return trim((string) $value); + } +} diff --git a/backend/tests/Unit/Services/BigFive/ResultPageV2/BigFiveResultPageV2CoreBodyPreviewTest.php b/backend/tests/Unit/Services/BigFive/ResultPageV2/BigFiveResultPageV2CoreBodyPreviewTest.php index 5cf49464e..dc512cdf6 100644 --- a/backend/tests/Unit/Services/BigFive/ResultPageV2/BigFiveResultPageV2CoreBodyPreviewTest.php +++ b/backend/tests/Unit/Services/BigFive/ResultPageV2/BigFiveResultPageV2CoreBodyPreviewTest.php @@ -6187,6 +6187,32 @@ public function test_runtime_freeze_classifier_ignores_bigfive_v2_en_parity_draf $this->assertSame([], $this->mbtiImpactingRuntimeChanges($changed, '', '')); } + public function test_runtime_freeze_classifier_ignores_w2_en_parity_result_draft_package(): void + { + $allowed = [ + 'backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/README.md', + 'backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/claim_boundary_report.json', + 'backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/content_assets.jsonl', + 'backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/duplicate_report.json', + 'backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/editorial_self_review.json', + 'backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/inventory_row_reconciliation.json', + 'backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/package_manifest.json', + 'backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/sha256_manifest.json', + 'backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/source_ledger.json', + 'backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/surface_coverage.json', + 'backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/translation_map.json', + 'backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/validate_package.php', + ]; + $blocked = [ + 'backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/unreviewed_runtime_payload.json', + 'backend/content_packs/BIG5_OCEAN/v2/registry/shared/trait_labels.json', + 'backend/app/Services/BigFive/ResultPageV2/BigFiveResultPageV2Service.php', + ]; + + $this->assertSame([], $this->mbtiImpactingRuntimeChanges($allowed, '', '')); + $this->assertSame($blocked, $this->mbtiImpactingRuntimeChanges($blocked, '', '')); + } + public function test_runtime_freeze_classifier_ignores_personality_public_asset_contract_changes(): void { $changed = [ @@ -7997,6 +8023,10 @@ private function mbtiImpactingRuntimeChanges( continue; } + if ($this->isBigFiveW2EnParityResultDraftPackageFile($file)) { + continue; + } + if ($this->isBigFiveEnglishDraftInventoryFile($file)) { continue; } @@ -11895,6 +11925,24 @@ private function isBigFiveV2EnParityDraftCatalogFile(string $file): bool return $file === 'backend/content_packs/BIG5_OCEAN/v2/drafts/en_parity/result_page_v2_en_asset_catalog_draft.v1.json'; } + private function isBigFiveW2EnParityResultDraftPackageFile(string $file): bool + { + return in_array($file, [ + 'backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/README.md', + 'backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/claim_boundary_report.json', + 'backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/content_assets.jsonl', + 'backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/duplicate_report.json', + 'backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/editorial_self_review.json', + 'backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/inventory_row_reconciliation.json', + 'backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/package_manifest.json', + 'backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/sha256_manifest.json', + 'backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/source_ledger.json', + 'backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/surface_coverage.json', + 'backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/translation_map.json', + 'backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/validate_package.php', + ], true); + } + private function isBigFiveEnglishDraftInventoryFile(string $file): bool { return in_array($file, [ diff --git a/docs/codex/pr-train-state.json b/docs/codex/pr-train-state.json index 69c731e8a..139a46787 100644 --- a/docs/codex/pr-train-state.json +++ b/docs/codex/pr-train-state.json @@ -102395,5 +102395,121 @@ } ], "updated_at": "2026-07-31T04:52:07Z" + }, + "EN-PARITY-W2-BIG5-RESULT-ASSETS-01": { + "id": "EN-PARITY-W2-BIG5-RESULT-ASSETS-01", + "repo": "fap-api", + "base": "main", + "branch": "codex/en-parity-w2-big5-result-assets-01", + "title": "EN-PARITY-W2-BIG5-RESULT-ASSETS-01: produce reviewed Big Five English result assets", + "status": "github_checks_pending", + "depends_on": [ + "EN-PARITY-W2-BIG5-INVENTORY-01" + ], + "commit_sha": "be8e157199b43d0431dde0d3ccf1c6cb084aa1bf", + "pr_url": "https://github.com/fermatmind/fap-api/pull/3451", + "checks": { + "dependency": { + "status": "pass", + "evidence": "fap-web PR 1866 merged as 897490e4baa31fe197ee50c89f0c3fae6bac408d. Its immutable 118-row inventory package SHA is 0f50f4108af14656442ef7d57d410b2e74f8dffced6ed3db372bf848ea051292; post-merge branch cleanup and local main synchronization passed." + }, + "content_package_contract": { + "status": "pass", + "evidence": "The existing Big Five English draft catalog supports draft-only reader label groups. The operator-frozen analytics_reader_labels identity is represented as reader copy with empty internal_metric_ids and personal_result_fields; no selector, runtime, importer, or schema change is required." + }, + "local_validation": { + "status": "pass", + "evidence": "Package validator passes twice byte-identically with SHA aea87a8c0545d1be6cb1a32ff981576d62d077a8eab36834f34ec9d41c1bfc81. Focused package PHPUnit passes 7 tests / 517 assertions with exact 118-row reconciliation, all-30 facet semantic anchors, required-key coverage, and complete package-file binding; runtime-freeze explicit-file regression passes 2 tests / 6 assertions with unknown package paths blocked; composer test:content passes 4 tests / 62 assertions. Scoped Pint, PHP syntax, JSON/JSONL/YAML parsing, CJK/private-field/public-URL/fixed-type/forbidden-claim scans, deterministic SHA recomputation, diff checks, and exact allowlist validation pass." + }, + "github_required_checks": { + "status": "pass", + "evidence": "Exact head 37d6bb473d8a0f8072e4920fd4c8813fa3ea47c2 passed all nine applicable checks in CI run 30611920168 and Code Scanning run 30611920174: hygiene, supply-chain, content-pack-build-validate, verify-mbti-legacy, verify-mbti-v2, verify-bigfive, Semgrep blocking secrets, Semgrep OSS, and semgrep SARIF upload. verify-staging-parity was skipped and is not required." + }, + "optional_codex_review": { + "status": "external_nonblocking", + "evidence": "An exact-head review request returned the explicit Codex usage-limit comment at 2026-07-31T07:19:29Z without generating a new review. The external account/service quota is recorded in generated/pr-train-sidecar-issues; repo policy does not require this review, GitHub reports CLEAN/MERGEABLE, and all prior review threads are resolved." + } + }, + "scope_validation": { + "allowed_paths_only": true, + "local_validation_passed": true, + "github_checks_green": true, + "post_merge_revalidated": false + }, + "failure_reason": null, + "merged_at": null, + "remote_branch_deleted": false, + "local_cleanup_executed": false, + "production_write_execution": false, + "production_read_only_evidence": false, + "database_mutation": false, + "cms_mutation": false, + "api_runtime_change": false, + "publish_allowed": false, + "search_channel_action": false, + "deploy_allowed": false, + "transitions": [ + { + "at": "2026-07-31T05:21:44Z", + "status": "implementation_in_progress", + "note": "Started from clean fap-api main d9f1a5554bb9831a1a9dd87d76b41daf8bcac353 after dependency PR 1866 merged and post-merge revalidated. Contract preflight proved all sixteen identities, including analytics reader labels, can be represented in a draft-only package without schema, selector, runtime, or importer changes." + }, + { + "at": "2026-07-31T05:34:22Z", + "status": "local_verified", + "note": "Produced exactly sixteen traceable English draft asset groups with 5 dimension and 30 facet explanations, value-neutral range copy, share/private-reader boundaries, fail-closed states, cross-device copy, and analytics reader labels separated from internal IDs. Deterministic package SHA is cda743f52cebaf753b8d0f2f234dd893e88f5d630ea6cc4e7a5f5b32faefda06. All required local package, focused, content-suite, formatting, parsing, scan, diff, and scope checks pass; no write or runtime authority changed." + }, + { + "at": "2026-07-31T05:35:51Z", + "status": "github_checks_pending", + "note": "Committed and pushed the exact sixteen-path scope as implementation head 6fc25b8a8247c660f969a1709dca4236276a9496, then opened PR 3451. Required GitHub checks and exact-head review are pending." + }, + { + "at": "2026-07-31T05:47:03Z", + "status": "review_fix_local_checks_passed", + "note": "Inspected CI run 30607268537 verify-bigfive before editing: 1658 tests passed and the only failure was the runtime-freeze classifier treating the new draft package directory as runtime-impacting. Added an exact package-prefix exception plus positive/negative regression and declared the classifier test in scope. Exact-head review findings are also repaired: local_validation_passed is true, baseline tree OIDs resolve from pinned commit d9f1a5554bb9831a1a9dd87d76b41daf8bcac353 rather than the live checkout, and N5 now preserves the source facet's overload-driven disconnect/withdraw/change-state meaning. Package SHA rebuilt as 42760c9d3b7ae3449e1eaacccbd7c1b3dbdf78cae42cfef44d0063f640a54dd7; all focused, content-suite, Pint, parser, scan, diff, and scope checks pass." + }, + { + "at": "2026-07-31T05:58:55Z", + "status": "review_fix_local_checks_passed", + "note": "Inspected the two exact-head review findings before editing. Restored an unrelated existing task's local_validation_passed value and set the flag only on this train item. N3 now preserves the source facet's sustained-pressure depletion meaning, with focused label and semantic regression assertions. The byte-exact package was rebuilt as 6543c9818a272330c55256a0359bce7ee0223591cb46d06436ac06245f6a2cc7. Validator passes twice identically; the package suite passes 6 tests / 244 assertions, runtime-freeze regression passes, content suite passes, and Pint, syntax, parser, scan, diff, and exact 17-path scope validation pass." + }, + { + "at": "2026-07-31T06:12:00Z", + "status": "review_fix_local_checks_passed", + "note": "After all nine applicable required checks passed on exact head 5253de1d373ecb4c948ad6bd4e1d3d04aa099043, exact-head review identified one remaining O3 semantic drift. O3 now preserves the pinned source authority's willingness to identify, approach, express, and use inner feelings as information, with three focused semantic assertions. The byte-exact package was rebuilt as ca7a77819b74f0b0127b2f01fc626c28120903ac56cbfc77cdc60ea4af789b70. Validator passes twice identically; package suite passes 6 tests / 247 assertions, content suite passes 4 tests / 62 assertions, and Pint, parsers, diff, and exact 17-path scope validation pass." + }, + { + "at": "2026-07-31T06:25:35Z", + "status": "review_fix_local_checks_passed", + "note": "After all nine applicable required checks passed on exact head 4798999c49381693d97e1be52f691d694c701bce, exact-head review identified one remaining N6 semantic drift. N6 now preserves the pinned source authority's visible coping-limit vulnerability under high pressure, loss of control, and complex demands, with three focused semantic assertions. The byte-exact package was rebuilt as 479d071448e0b3385a3814d2256c4c084574746dcb2413dbf4ecb4107a221908. Validator passes twice identically; package suite passes 6 tests / 250 assertions, content suite passes 4 tests / 62 assertions, and Pint, parsers, diff, and exact 17-path scope validation pass." + }, + { + "at": "2026-07-31T06:33:53Z", + "status": "review_fix_local_checks_passed", + "note": "Exact-head review on f72ca150846d4d31072cc6866200c23c8764aa57 identified one remaining C5 semantic drift. C5 now preserves the pinned source authority's continued progress without immediate feedback or reward, with three focused semantic assertions. The byte-exact package was rebuilt as 76c547083230ae57c7028037bfb50623bdfd7cf007335166ac01abde1cf66851. Validator passes twice identically; package suite passes 6 tests / 253 assertions, content suite passes 4 tests / 62 assertions, and Pint, parsers, diff, and exact 17-path scope validation pass." + }, + { + "at": "2026-07-31T06:48:30Z", + "status": "review_fix_local_checks_passed", + "note": "Exact-head review on 675e96810e1c2359c67cf07edfdf9e5ce6b6fe81 identified three current-scope gaps. Added fail-closed reconciliation for all 118 immutable source rows, pinned the source-ledger and ordered row-id-set hashes, preserved the 52/50 completed-control and historical cohorts, and mapped every one of the 16 result rows to an exact asset ID, required top-level keys, and nested item identities. A4 now preserves conflict mediation and de-escalation; N2 preserves anger/resistance activation under obstruction, misunderstanding, or boundary violation. Package SHA is b6676eff4e63f257a7a460f5c79114f79ce23bc5cd54ccaf06ff835d5c635e6c. Validator passes twice; package suite passes 7 tests / 433 assertions, runtime-freeze regression and content suite pass, and Pint, syntax, parsers, scans, diff, and exact allowlist validation pass." + }, + { + "at": "2026-07-31T06:57:44Z", + "status": "review_fix_local_checks_passed", + "note": "Exact-head review on cf28114bff98d84d71cb72dfbec3bb1c60723830 identified two package-integrity gaps. The validator and focused test now require the actual package directory file set to equal every SHA-manifest entry plus sha256_manifest.json, so unbound extra files fail closed. The runtime-freeze exception now allowlists only the twelve reviewed draft-package files and blocks unknown future files under the same directory. Package SHA is 47d43575aea0c638a21b90c33e1f0c147e4f679986d76db182c0f28606cf4dd4. Validator passes twice; package suite passes 7 tests / 434 assertions, explicit classifier regression passes 2 tests / 6 assertions, content suite passes, and Pint, parsers, diff, and scope checks pass." + }, + { + "at": "2026-07-31T07:09:01Z", + "status": "review_fix_local_checks_passed", + "note": "Exact-head review on 65b8cd1f09d16b03153a5a1468bd2567ac6e32b4 identified E2 group-participation and N4 social-discomfort drift. Reconciled all 30 facet descriptions directly against the pinned glossary and added per-code semantic anchors enforced by both validator and focused tests, so future drift in any facet fails closed. E2 now covers multi-person settings, social density, and energy from groups; N4 covers discomfort, self-monitoring, and withdrawal under gaze, evaluation, or social exposure. Package SHA is aea87a8c0545d1be6cb1a32ff981576d62d077a8eab36834f34ec9d41c1bfc81. Validator passes twice; package suite passes 7 tests / 517 assertions, classifier and content suites pass, and Pint, parsers, diff, and scope checks pass." + }, + { + "at": "2026-07-31T07:23:51Z", + "status": "ready_to_merge", + "note": "Exact head 37d6bb473d8a0f8072e4920fd4c8813fa3ea47c2, containing semantic implementation head be8e157199b43d0431dde0d3ccf1c6cb084aa1bf and immutable package SHA aea87a8c0545d1be6cb1a32ff981576d62d077a8eab36834f34ec9d41c1bfc81, passed all nine applicable required checks. All prior review threads are resolved. A new optional Codex review could not be generated because the external account review quota was exhausted; the non-required blocker is recorded in the sidecar, repo policy requires no review, and GitHub reports CLEAN/MERGEABLE." + } + ], + "updated_at": "2026-07-31T07:23:51Z" } } diff --git a/docs/codex/pr-train.yaml b/docs/codex/pr-train.yaml index 4e9d91288..4056fc071 100644 --- a/docs/codex/pr-train.yaml +++ b/docs/codex/pr-train.yaml @@ -111,6 +111,60 @@ prs: deploy_allowed: false content_authority: backend + - id: EN-PARITY-W2-BIG5-RESULT-ASSETS-01 + repo: fap-api + base: main + branch: codex/en-parity-w2-big5-result-assets-01 + title: "EN-PARITY-W2-BIG5-RESULT-ASSETS-01: produce reviewed Big Five English result assets" + status: ready_to_merge + depends_on: + - EN-PARITY-W2-BIG5-INVENTORY-01 + train_scope: en_parity_w2_big5_result_assets + scope: + - Produce a backend-authoritative English draft-review-only content package for the sixteen frozen Big Five result/report/share/PDF/history inventory units. + - Preserve continuous, contextual, probabilistic, value-neutral Big Five language and private-reader/share-safe boundaries. + - Record deterministic package evidence and focused contract coverage without activating any runtime consumer. + allowed_paths: + - backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/** + - backend/docs/seo/generated/en-parity-w2-big-five-result-assets.v1.json + - backend/docs/seo/en-parity-w2-big-five-result-assets.md + - backend/tests/Feature/Content/BigFiveEnglishResultAssetPackageTest.php + - backend/tests/Unit/Services/BigFive/ResultPageV2/BigFiveResultPageV2CoreBodyPreviewTest.php + - docs/codex/pr-train.yaml + - docs/codex/pr-train-state.json + - generated/pr-train-sidecar-issues/sidecar_issues.md + - generated/pr-train-sidecar-issues/sidecar_issues.json + do_not: + - Modify routes, migrations, auth middleware, ResultPageV2 runtime services, selectors, importers, publishers, schemas, production commands, public APIs, or fap-web. + - Write CMS or database state, import, publish, promote, change indexability, mutate sitemap or llms, submit search URLs, or deploy. + - Claim human review, editorial approval, W9 independent QA, qa_pass, package_frozen, runtime readiness, or production readiness. + validation: + - php -l backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/validate_package.php + - php -l backend/tests/Feature/Content/BigFiveEnglishResultAssetPackageTest.php + - php backend/content_packs/BIG5_OCEAN/v2/packages/en_parity/w2_result_content_v1/validate_package.php + - cd backend && php artisan test tests/Feature/Content/BigFiveEnglishResultAssetPackageTest.php --no-ansi + - cd backend && composer test:content + - parse all package JSON and JSONL + - recompute every SHA manifest entry and aggregate package SHA + - scan for CJK, private fields, forbidden claims, fixed-type language, and public URLs + - ruby -e "require 'yaml'; YAML.load_file('docs/codex/pr-train.yaml'); puts 'yaml ok'" + - python3 -m json.tool docs/codex/pr-train-state.json >/dev/null + - git diff --check + - git diff --cached --check + - exact changed-file scope validation + stop_if_changed_files_outside_scope: true + merge_policy: { github_checks_required: true, squash: true, auto_merge: true } + production_write_execution: false + production_read_only_evidence: false + database_mutation: false + cms_mutation: false + api_runtime_change: false + publish_allowed: false + search_channel_action: false + deploy_allowed: false + content_authority: backend_draft_package + repository_rule_impact: draft-only backend content assets; existing authority, runtime, selector, importer, schema, and frontend no-fallback rules remain unchanged + - id: API-UNLOCK-01 repo: fap-api base: main diff --git a/generated/pr-train-sidecar-issues/sidecar_issues.json b/generated/pr-train-sidecar-issues/sidecar_issues.json index c68869365..ba54aa6a1 100644 --- a/generated/pr-train-sidecar-issues/sidecar_issues.json +++ b/generated/pr-train-sidecar-issues/sidecar_issues.json @@ -553,5 +553,22 @@ "recommended_follow_up": "This PR was rebased on the merged repair and must rerun its complete declared local validation and required GitHub checks.", "status": "repaired_by_independent_baseline_pr", "train_continued": true + }, + { + "observed_at": "2026-07-31T07:23:51Z", + "repo": "fap-api", + "pr_id": "EN-PARITY-W2-BIG5-RESULT-ASSETS-01 / PR #3451", + "branch": "codex/en-parity-w2-big5-result-assets-01", + "blocker_type": "external_codex_code_review_quota_exhausted", + "evidence": [ + "The exact-head Codex review request for 37d6bb473d8a0f8072e4920fd4c8813fa3ea47c2 returned an explicit usage-limit comment at 2026-07-31T07:19:29Z and did not generate a new code review.", + "CI run 30611920168 and Code Scanning run 30611920174 completed with all nine applicable checks passing; verify-staging-parity was skipped and is not a required check.", + "GitHub reports the PR CLEAN/MERGEABLE with no required review decision, and every previously generated review thread is resolved." + ], + "why_not_current_pr_scope": "The exhausted Codex review allowance is an account/service quota outside the draft package, tests, and repository metadata changed by this PR.", + "required_checks_affected": false, + "recommended_follow_up": "Replenish or enable Codex review credits before a future optional review request. No code change is required: the exact-head package already enforces all-30 facet semantic anchors and all earlier review findings are resolved.", + "status": "recorded_external_nonblocking", + "train_continued": true } ] diff --git a/generated/pr-train-sidecar-issues/sidecar_issues.md b/generated/pr-train-sidecar-issues/sidecar_issues.md index 7843d56c1..1ae552962 100644 --- a/generated/pr-train-sidecar-issues/sidecar_issues.md +++ b/generated/pr-train-sidecar-issues/sidecar_issues.md @@ -623,3 +623,22 @@ - This PR was rebased on the merged repair and must rerun its complete declared local validation and required GitHub checks. - status: `repaired_by_independent_baseline_pr` - whether train continued: `true` + +## W2 Big Five result-assets optional Codex review quota + +- observed at: `2026-07-31T07:23:51Z` +- repo: `fap-api` +- PR id / branch: `EN-PARITY-W2-BIG5-RESULT-ASSETS-01` / PR #3451 / `codex/en-parity-w2-big5-result-assets-01` +- blocker type: `external_codex_code_review_quota_exhausted` +- evidence: + - The exact-head Codex review request for `37d6bb473d8a0f8072e4920fd4c8813fa3ea47c2` returned an explicit usage-limit comment at `2026-07-31T07:19:29Z` and did not generate a new code review. + - CI run `30611920168` and Code Scanning run `30611920174` completed with all nine applicable checks passing; `verify-staging-parity` was skipped and is not required. + - GitHub reports the PR CLEAN/MERGEABLE with no required review decision, and every previously generated review thread is resolved. +- why not current PR scope: + - The exhausted Codex review allowance is an account/service quota outside the draft package, tests, and repository metadata changed by this PR. +- whether required checks are affected: `false` +- recommended follow-up: + - Replenish or enable Codex review credits before a future optional review request. + - No code change is required: the exact-head package already enforces all-30 facet semantic anchors and all earlier review findings are resolved. +- status: `recorded_external_nonblocking` +- whether train continued: `true`