{
  "version": "V7",
  "generated_at": "2026-07-17T04:03:43.228Z",
  "status": "non_promoted_forecast_readiness_layer",
  "description": "Source, duplication, and validation protocol matrix for moving from comparative V8 AI exposure to forecast-grade labour-market claims. This artifact does not change the headline V8 ranks.",
  "non_duplication_policy": {
    "headline_score_mutated": false,
    "realized_risk_score_created": false,
    "decision": "Do not create a second realised-risk model while labour-monitor.json, postings-monitor.json, ai-in-singapore.json, and the V8 context fields already exist. Forecast-readiness references those owners and records missing validation gates.",
    "existing_artifacts_reused": [
      "data/labour-monitor.json",
      "data/postings/postings-monitor.json",
      "data/ai-in-singapore.json",
      "data/v5-realized-risk.json",
      "data/v5-experimental-validation.json",
      "data/backtests/multi-period-validation.json"
    ]
  },
  "summary": {
    "occupation_count": 562,
    "labour_monitor_cluster_count": 3,
    "postings_total": 564,
    "posting_source_count": 4,
    "postings_latest_posted_date": "2026-03-20T05:39:49.826Z",
    "postings_observed_through": "2026-03-20T05:39:49.826Z",
    "postings_occupation_coverage": "93/562",
    "postings_role_coverage": "42/88",
    "mom_ai_adoption_report_published_at": "2026-04-30",
    "mom_ai_adopting_firms_pct": 28.5,
    "status_counts": {
      "ready_for_directional_validation": 5,
      "partial_proxy_needs_snapshots": 2,
      "mapped_context_only": 1,
      "harness_built_pending_quarters": 1
    }
  },
  "inputs": [
    {
      "key": "vacancy_trends",
      "label": "Vacancy trend",
      "construct": "realised_labour_pressure",
      "status": "ready_for_directional_validation",
      "evidence_tier": "derived_from_official_sg",
      "source_keys": [
        "mom_job_vacancy_rates",
        "mom_job_vacancy_counts"
      ],
      "source_urls": [
        "https://data.gov.sg/datasets?resultId=d_1e10046c33418c507bb2483c26dca489&sort=updatedAt",
        "https://data.gov.sg/datasets?resultId=d_86dffa3d28e4c3ee085c3f99abad6e9f&sort=updatedAt"
      ],
      "raw_files": [
        "data/raw/vacancy_rates_by_occupation_group.csv",
        "data/raw/job_vacancies_by_industry_and_occupation_quarterly.csv"
      ],
      "existing_artifacts": [
        "data/labour-monitor.json",
        "data/backtests/multi-period-validation.json"
      ],
      "pipeline_owners": [
        "scripts/build-labour-monitor.ts",
        "scripts/backtest-multi-period.ts"
      ],
      "public_fields": [
        "labour-monitor.vacancy.trend_4q_pct",
        "labour-monitor.vacancy.count_trend_4q_pct"
      ],
      "transformation": "Quarterly and four-quarter movement are computed from official MOM vacancy-rate and vacancy-count series, with the latest report enrichment retained in the labour monitor.",
      "granularity": "Broad occupation clusters and industry/occupation aggregates; not detailed 5-digit SSOC vacancy truth.",
      "confidence_for_forecast": "strong_directional",
      "non_duplication_rule": "Reuse the existing labour-monitor and multi-period validation owners; do not create a second vacancy monitor.",
      "next_step": "Use as an outcome in q+1, q+2, and q+4 forecast-horizon tests against frozen structural snapshots."
    },
    {
      "key": "vacancy_rates",
      "label": "Vacancy rate",
      "construct": "realised_labour_pressure",
      "status": "ready_for_directional_validation",
      "evidence_tier": "official_sg",
      "source_keys": [
        "mom_job_vacancy_rates",
        "mom_labour_market_report_q1_2026"
      ],
      "source_urls": [
        "https://data.gov.sg/datasets?resultId=d_1e10046c33418c507bb2483c26dca489&sort=updatedAt",
        "https://stats.mom.gov.sg/Pages/Labour-Market-Report-1Q-2026.aspx"
      ],
      "raw_files": [
        "data/raw/vacancy_rates_by_occupation_group.csv"
      ],
      "existing_artifacts": [
        "data/labour-monitor.json"
      ],
      "pipeline_owners": [
        "scripts/build-labour-monitor.ts"
      ],
      "public_fields": [
        "labour-monitor.vacancy.latest_rate",
        "labour-monitor.vacancy.latest_quarter"
      ],
      "transformation": "Direct published vacancy-rate series, with Q1 2026 report-table enrichment where the feed lagged at build time.",
      "granularity": "Broad occupation clusters.",
      "confidence_for_forecast": "strong_directional",
      "non_duplication_rule": "Keep vacancy rates as labour-monitor fields and source-map entries; do not copy them into occupation records.",
      "next_step": "Add an outcome panel that stores rates by quarter for each cluster and frozen score vintage."
    },
    {
      "key": "recruitment_minus_resignation",
      "label": "Recruitment minus resignation",
      "construct": "realised_labour_pressure",
      "status": "ready_for_directional_validation",
      "evidence_tier": "derived_from_official_sg",
      "source_keys": [
        "mom_recruitment_resignation_rates",
        "mom_labour_market_report_q1_2026"
      ],
      "source_urls": [
        "https://data.gov.sg/collections/682/datasets/d_236436f8bdb9bbac677c4e5637c6430e/view",
        "https://stats.mom.gov.sg/Pages/Labour-Market-Report-1Q-2026.aspx"
      ],
      "raw_files": [
        "data/raw/recruitment_resignation_rates.json"
      ],
      "existing_artifacts": [
        "data/labour-monitor.json"
      ],
      "pipeline_owners": [
        "scripts/download-official-sg-labour.ts",
        "scripts/build-labour-monitor.ts"
      ],
      "public_fields": [
        "labour-monitor.hiring.recruitment_rate",
        "labour-monitor.hiring.net_pressure"
      ],
      "transformation": "Deterministic net pressure: recruitment_rate minus resignation_rate from official MOM/SingStat series or current-quarter report enrichment.",
      "granularity": "Broad occupation clusters, with annual/quarterly availability depending on the source feed.",
      "confidence_for_forecast": "strong_directional",
      "non_duplication_rule": "Keep the computed net pressure in the labour monitor; forecast tests should reference it by source field.",
      "next_step": "Use net pressure as a secondary realised-labour outcome, not as a headline-score multiplier."
    },
    {
      "key": "retrenchment_incidence",
      "label": "Retrenchment incidence",
      "construct": "realised_labour_pressure",
      "status": "ready_for_directional_validation",
      "evidence_tier": "official_sg",
      "source_keys": [
        "mom_retrenchment_by_occupation_group",
        "mom_labour_market_report_q1_2026"
      ],
      "source_urls": [
        "https://data.gov.sg/datasets?resultId=d_3eaf52cdcc405a80b602d031d0bd092b&sort=updatedAt",
        "https://stats.mom.gov.sg/Pages/Labour-Market-Report-1Q-2026.aspx"
      ],
      "raw_files": [
        "data/raw/retrenchment_by_occupation_group.json"
      ],
      "existing_artifacts": [
        "data/labour-monitor.json"
      ],
      "pipeline_owners": [
        "scripts/download-official-sg-labour.ts",
        "scripts/build-labour-monitor.ts"
      ],
      "public_fields": [
        "labour-monitor.retrenchment.latest_count",
        "labour-monitor.retrenchment.incidence_per_1000"
      ],
      "transformation": "Counts come from the official quarterly retrenchment series; incidence per 1,000 employees comes from the MOM labour-market report table.",
      "granularity": "Broad occupation clusters, not 5-digit SSOC occupations.",
      "confidence_for_forecast": "strong_directional",
      "non_duplication_rule": "Keep retrenchment as a labour-monitor outcome and do not expose it as occupation-level displacement evidence.",
      "next_step": "Include as a low-frequency stress signal in forecast-horizon validation."
    },
    {
      "key": "wage_movement",
      "label": "Wage movement",
      "construct": "realised_labour_pressure",
      "status": "ready_for_directional_validation",
      "evidence_tier": "official_sg",
      "source_keys": [
        "mom_median_income_by_occupation"
      ],
      "source_urls": [
        "https://data.gov.sg/datasets/d_8f024ddf2553d81ee00ede55b1d9b0ff/view"
      ],
      "raw_files": [
        "data/raw/median_income_by_occupation.csv"
      ],
      "existing_artifacts": [
        "data/wage-movement.json"
      ],
      "pipeline_owners": [
        "scripts/build-wage-movement.ts"
      ],
      "public_fields": [
        "wage-movement.series.*.movement"
      ],
      "transformation": "Sex-specific broad-group median income is converted to 1-, 3-, and 5-year nominal and All Items CPI-adjusted movement. Male and female medians remain separate.",
      "granularity": "Broad occupation groups and sex splits; annual, sample-survey based.",
      "confidence_for_forecast": "medium_directional",
      "non_duplication_rule": "Do not duplicate gross_wage_median or sector wage anchors; build a separate wage-outcome transform only if it becomes a validation outcome.",
      "next_step": "Keep the annual broad-group sidecar separate from quarterly cluster panels and test it only at matching granularity."
    },
    {
      "key": "postings_volume",
      "label": "Postings volume",
      "construct": "realised_labour_pressure",
      "status": "partial_proxy_needs_snapshots",
      "evidence_tier": "external_proxy",
      "source_keys": [
        "sg_postings_monitor"
      ],
      "source_urls": [
        "https://www.mycareersfuture.gov.sg/"
      ],
      "raw_files": [
        "data/postings/raw/*.json",
        "data/postings/source-registry.json"
      ],
      "existing_artifacts": [
        "data/postings/postings-monitor.json"
      ],
      "pipeline_owners": [
        "scripts/pipelines/fetch-postings.ts",
        "scripts/pipelines/normalize-postings.ts"
      ],
      "public_fields": [
        "postings-monitor.summary.posting_volume_30d",
        "postings-monitor.by_ssoc.*.posting_volume_90d"
      ],
      "transformation": "Observed postings are normalized from MyCareersFuture and employer ATS snapshots into 30-day/90-day volume and trend fields.",
      "granularity": "Occupation-level where title/alias matching succeeds, but source coverage is partial and the current snapshot is stale-sensitive.",
      "confidence_for_forecast": "weak_until_refreshed",
      "non_duplication_rule": "Use the existing postings monitor; add scheduled snapshots rather than creating another postings dataset.",
      "next_step": "Schedule monthly snapshots and deduplicate stable source IDs before using postings volume as a realised outcome."
    },
    {
      "key": "ai_skill_share_in_postings",
      "label": "AI-skill share in postings",
      "construct": "near_term_adoption",
      "status": "partial_proxy_needs_snapshots",
      "evidence_tier": "external_proxy",
      "source_keys": [
        "sg_postings_monitor",
        "imda_sgde_2025"
      ],
      "source_urls": [
        "https://www.mycareersfuture.gov.sg/",
        "https://www.imda.gov.sg/resources/press-releases-factsheets-and-speeches/press-releases/2025/singapore-digital-economy"
      ],
      "raw_files": [
        "data/postings/raw/*.json"
      ],
      "existing_artifacts": [
        "data/postings/postings-monitor.json"
      ],
      "pipeline_owners": [
        "scripts/pipelines/fetch-postings.ts",
        "scripts/pipelines/normalize-postings.ts"
      ],
      "public_fields": [
        "postings-monitor.summary.top_tools",
        "postings-monitor.by_ssoc.*.top_tools"
      ],
      "transformation": "Raw postings already carry ai_tools_mentioned and top_tools counts, but an AI-skill share needs a fixed dictionary, denominator, and time series.",
      "granularity": "Occupation/role proxy where postings match; not official statistics.",
      "confidence_for_forecast": "weak_until_refreshed",
      "non_duplication_rule": "Extend postings-monitor fields with shares after snapshots exist; do not build a separate AI-skills crawler.",
      "next_step": "Add a versioned AI-skill dictionary and compute share = AI-skill postings / matched postings by month and occupation."
    },
    {
      "key": "firm_ai_adoption",
      "label": "Firm AI adoption",
      "construct": "near_term_adoption",
      "status": "mapped_context_only",
      "evidence_tier": "official_sg",
      "source_keys": [
        "mom_ai_adoption_2026",
        "imda_sgde_2025"
      ],
      "source_urls": [
        "https://www.mom.gov.sg/newsroom/press-releases/2026/0430-adoption-of-ai-among-firms",
        "https://www.imda.gov.sg/resources/press-releases-factsheets-and-speeches/press-releases/2025/singapore-digital-economy"
      ],
      "raw_files": [
        "data/raw/mom-ai-adoption-2026.json"
      ],
      "existing_artifacts": [
        "data/ai-in-singapore.json",
        "data/adoption-diffusion.json",
        "data/occupations.json"
      ],
      "pipeline_owners": [
        "scripts/build-ai-in-singapore.ts",
        "scripts/build-adoption-diffusion.ts",
        "scripts/build-v8-release.ts"
      ],
      "public_fields": [
        "occupations.*.v8.market_context.adoption",
        "occupations.*.v8.market_context.adoption_coverage"
      ],
      "transformation": "Official sector adoption is mapped through each occupation major group industry footprint, with explicit partial or unavailable coverage. It remains contextual and does not alter exposure ranks.",
      "granularity": "Firm-size and broad sector, not occupation-level.",
      "confidence_for_forecast": "medium_directional",
      "non_duplication_rule": "Reuse the V8 market-context mapping and do not multiply adoption into occupation exposure or substitution ranks.",
      "next_step": "Refresh the existing context mapping when MOM publishes a new sector adoption table; do not promote it as observed occupation adoption."
    },
    {
      "key": "forecast_horizon_protocol",
      "label": "Out-of-sample forecast protocol",
      "construct": "validation_protocol",
      "status": "harness_built_pending_quarters",
      "evidence_tier": "synthetic",
      "source_keys": [],
      "source_urls": [],
      "raw_files": [
        "data/snapshots/occupations-v7-2026-05.json",
        "data/outcomes/outcome-panels.json"
      ],
      "existing_artifacts": [
        "data/backtests/multi-period-validation.json",
        "data/backtests/forecast-horizon-validation.json"
      ],
      "pipeline_owners": [
        "scripts/backtest-multi-period.ts",
        "scripts/build-outcome-panels.ts",
        "scripts/backtest-forecast-horizons.ts"
      ],
      "public_fields": [
        "forecast-readiness.validation_protocol"
      ],
      "transformation": "Freeze structural scores at time t and compare against official outcome panels at t+1Q, t+2Q, and t+4Q.",
      "granularity": "Protocol supports broad cluster outcomes now and can expand to occupation-level outcomes only when reliable data exists.",
      "confidence_for_forecast": "not_ready",
      "non_duplication_rule": "Extend the existing backtest family with forecast horizons; do not treat current cluster validation as a causal forecast.",
      "next_step": "Harness and outcome panels are built with post_baseline_quarters_available = 0; horizons activate as MOM publishes Q3 2026+ data, with promotion gated on at least 4 post-baseline quarters."
    }
  ],
  "file_health": [
    {
      "key": "vacancy_trends",
      "checked": [
        {
          "file": "data/raw/vacancy_rates_by_occupation_group.csv",
          "exists": true
        },
        {
          "file": "data/raw/job_vacancies_by_industry_and_occupation_quarterly.csv",
          "exists": true
        },
        {
          "file": "data/labour-monitor.json",
          "exists": true
        },
        {
          "file": "data/backtests/multi-period-validation.json",
          "exists": true
        }
      ],
      "all_present": true
    },
    {
      "key": "vacancy_rates",
      "checked": [
        {
          "file": "data/raw/vacancy_rates_by_occupation_group.csv",
          "exists": true
        },
        {
          "file": "data/labour-monitor.json",
          "exists": true
        }
      ],
      "all_present": true
    },
    {
      "key": "recruitment_minus_resignation",
      "checked": [
        {
          "file": "data/raw/recruitment_resignation_rates.json",
          "exists": true
        },
        {
          "file": "data/labour-monitor.json",
          "exists": true
        }
      ],
      "all_present": true
    },
    {
      "key": "retrenchment_incidence",
      "checked": [
        {
          "file": "data/raw/retrenchment_by_occupation_group.json",
          "exists": true
        },
        {
          "file": "data/labour-monitor.json",
          "exists": true
        }
      ],
      "all_present": true
    },
    {
      "key": "wage_movement",
      "checked": [
        {
          "file": "data/raw/median_income_by_occupation.csv",
          "exists": true
        },
        {
          "file": "data/wage-movement.json",
          "exists": true
        }
      ],
      "all_present": true
    },
    {
      "key": "postings_volume",
      "checked": [
        {
          "file": "data/postings/source-registry.json",
          "exists": true
        },
        {
          "file": "data/postings/postings-monitor.json",
          "exists": true
        }
      ],
      "all_present": true
    },
    {
      "key": "ai_skill_share_in_postings",
      "checked": [
        {
          "file": "data/postings/postings-monitor.json",
          "exists": true
        }
      ],
      "all_present": true
    },
    {
      "key": "firm_ai_adoption",
      "checked": [
        {
          "file": "data/raw/mom-ai-adoption-2026.json",
          "exists": true
        },
        {
          "file": "data/ai-in-singapore.json",
          "exists": true
        },
        {
          "file": "data/adoption-diffusion.json",
          "exists": true
        },
        {
          "file": "data/occupations.json",
          "exists": true
        }
      ],
      "all_present": true
    },
    {
      "key": "forecast_horizon_protocol",
      "checked": [
        {
          "file": "data/snapshots/occupations-v7-2026-05.json",
          "exists": true
        },
        {
          "file": "data/outcomes/outcome-panels.json",
          "exists": true
        },
        {
          "file": "data/backtests/multi-period-validation.json",
          "exists": true
        },
        {
          "file": "data/backtests/forecast-horizon-validation.json",
          "exists": true
        }
      ],
      "all_present": true
    }
  ],
  "validation_protocol": {
    "claim_boundary": "V8 may claim comparative AI exposure and publish separate directional labour context. It should not claim forecast-grade occupation displacement until the gates below pass out of sample.",
    "score_freeze": "Use data/snapshots/occupations-v7-2026-05.json as the first V7 frozen baseline.",
    "horizons": [
      "t+1Q",
      "t+2Q",
      "t+4Q"
    ],
    "outcomes": [
      "vacancy_trends",
      "vacancy_rates",
      "recruitment_minus_resignation",
      "retrenchment_incidence",
      "wage_movement",
      "postings_volume",
      "ai_skill_share_in_postings"
    ],
    "metrics": [
      "Spearman correlation by outcome",
      "top-quintile lift versus bottom quintile",
      "pairwise accuracy by cluster/family",
      "calibration by risk band",
      "false-positive and false-negative anchor review"
    ],
    "promotion_gates": [
      "At least four observed quarters after the frozen score baseline.",
      "High-risk groups show weaker vacancy or postings movement than low-risk groups in at least two horizons.",
      "High-confidence score segments outperform low-confidence segments.",
      "Any combined forecast layer remains a sidecar until source granularity and validation results are published.",
      "No occupation page presents cluster-level outcomes as detailed SSOC evidence."
    ]
  },
  "next_steps": [
    "Do not alter V8 headline ranks with outcome context.",
    "Materialize outcome panels under data/outcomes/ only from existing official labour/postings owners.",
    "Add monthly postings snapshots before computing AI-skill share trends.",
    "Forecast-horizon harness is built (status pending_sufficient_quarters); rerun as each MOM quarter lands and promote only past the 4-quarter gate.",
    "Promote any forecast layer only as a separate sidecar with confidence labels and source granularity."
  ]
}