diff --git a/.github/workflows/cd-staging.yml b/.github/workflows/cd-staging.yml index 333c741f..de2d7c38 100644 --- a/.github/workflows/cd-staging.yml +++ b/.github/workflows/cd-staging.yml @@ -168,6 +168,7 @@ jobs: data_agent/test_chongqing_real_source_admission.py \ data_agent/test_chongqing_extraction_provenance.py \ data_agent/test_chongqing_source_governance.py \ + data_agent/test_chongqing_admission_readiness.py \ data_agent/test_dolphinscheduler_adapter.py \ data_agent/test_dolphinscheduler_command_consumer.py \ data_agent/test_dolphinscheduler_command_worker.py \ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 86db6f86..8147f03e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -133,6 +133,9 @@ jobs: - name: Validate Chongqing source governance evidence run: python -m data_agent.chongqing_source_governance + - name: Validate Chongqing admission readiness evidence + run: python -m data_agent.chongqing_admission_readiness + - name: Validate DolphinScheduler adapter boundary run: python -m data_agent.dolphinscheduler_adapter validate @@ -171,6 +174,7 @@ jobs: data_agent/test_chongqing_real_source_admission.py \ data_agent/test_chongqing_extraction_provenance.py \ data_agent/test_chongqing_source_governance.py \ + data_agent/test_chongqing_admission_readiness.py \ data_agent/test_dolphinscheduler_adapter.py \ data_agent/test_dolphinscheduler_command_consumer.py \ data_agent/test_dolphinscheduler_command_worker.py \ diff --git a/data_agent/chongqing_admission_readiness.py b/data_agent/chongqing_admission_readiness.py new file mode 100644 index 00000000..b19758f7 --- /dev/null +++ b/data_agent/chongqing_admission_readiness.py @@ -0,0 +1,396 @@ +"""Validate the fail-closed readiness contract before Chongqing admission. + +M3-31 binds the M3-28 physical baseline, M3-29 derivation-gap record and +M3-30 governance baseline into one metadata-only admission readiness profile. +The checked profile records every missing external requirement and never reads +or copies source payloads or creates Landing, Run, scheduler or provider +authority. +""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import re +from collections.abc import Mapping +from pathlib import Path +from typing import Any + +from . import chongqing_extraction_provenance as provenance +from . import chongqing_real_source_admission as admission +from . import chongqing_source_governance as governance + +EVIDENCE_SCHEMA = "gda.chongqing_admission_readiness.v1" +VALIDATION_SCHEMA = "gda.chongqing_admission_readiness_validation.v1" +STATUS = "blocked_pending_protected_admission_attestation" +SOURCE_ID = admission.SOURCE_ID +SOURCE_GROUP_ID = governance.SOURCE_GROUP_ID +ASSET_ID = governance.ASSET_ID +SOURCE_REF = governance.SOURCE_REF + +REPO_ROOT = Path(__file__).resolve().parent.parent +DEFAULT_EVIDENCE_PATH = REPO_ROOT / ( + "docs/evidence/chongqing-admission-readiness-2026-08-17.json" +) + +UPSTREAM_ADMISSION_EVIDENCE_SHA256 = ( + "a2196495d845d61be939c7fc36a7f05c3567e365599d2d04be0aab9c568459c1" +) +UPSTREAM_ADMISSION_FILE_SHA256 = admission.EVIDENCE_FILE_SHA256 +UPSTREAM_PROVENANCE_EVIDENCE_SHA256 = ( + "b56ce0c036827d4338ab2cfae8f3fb4c9e1e78ec18aac243272f1f77801300ef" +) +UPSTREAM_PROVENANCE_FILE_SHA256 = provenance.EVIDENCE_FILE_SHA256 +UPSTREAM_GOVERNANCE_EVIDENCE_SHA256 = ( + "97cf11ab8938c048dce9db903d1a4f30758f208dec6dad1a08b740a4a8fe7b6f" +) +UPSTREAM_GOVERNANCE_FILE_SHA256 = governance.EVIDENCE_FILE_SHA256 +EVIDENCE_FILE_SHA256 = ( + "c595065e152988529ff12e2301d59caebb31d2889658a676c9d1f8239e6f8372" +) + +SHA256_PATTERN = re.compile(r"^[0-9a-f]{64}$") +REQUIREMENT_KEYS = ( + "operator_identity", + "tool_version", + "command_digest", + "modified_entry_manifest", + "additional_entry_manifest", + "archive_to_working_set_attestation", + "owner_decision", + "license_decision", + "retention_decision", + "access_decision", + "privacy_sensitivity_decision", + "standard_version_decision", + "data_slo_decision", + "golden_result_decision", + "fresh_protected_attestation", +) +REQUIREMENT_RECORD_INVENTORY = {"source", "status", "attestation_sha256"} +REQUIREMENT_STATUSES = {"missing", "provided", "verified", "rejected"} + +EVIDENCE_INVENTORY = { + "schema", + "status", + "captured_at", + "source_binding", + "required_evidence", + "blockers", + "admission_policy", + "claims", + "evidence_sha256", +} +SOURCE_BINDING_INVENTORY = { + "source_id", + "source_group_id", + "asset_id", + "source_ref", + "upstream_admission_evidence_sha256", + "upstream_admission_evidence_file_sha256", + "upstream_provenance_evidence_sha256", + "upstream_provenance_evidence_file_sha256", + "upstream_governance_evidence_sha256", + "upstream_governance_evidence_file_sha256", + "archive_sha256", + "extracted_payload_sha256", + "source_payload_in_evidence", + "absolute_source_paths_in_evidence", +} +ADMISSION_POLICY_INVENTORY = { + "metadata_readiness_record_allowed", + "admission_requires_complete_derivation", + "admission_requires_complete_governance", + "admission_requires_fresh_protected_attestation", + "source_payload_copy_to_repository_forbidden", + "local_profile_is_not_production_admission", + "content_admission_authorized", + "landing_authority_creation_allowed", + "resource_version_creation_allowed", + "platform_run_creation_allowed", + "scheduler_submission_allowed", + "provider_mutation_allowed", +} +CLAIMS = { + "upstream_evidence_bound", + "derivation_attestation_complete", + "governance_decisions_complete", + "fresh_protected_attestation_valid", + "admission_eligible", + "source_content_admitted", + "landing_authority_created", + "resource_version_created", + "platform_run_created", + "scheduler_submission_authorized", + "provider_mutation_authorized", + "production_ingestion_verified", + "production_ready", +} + + +class ChongqingAdmissionReadinessError(RuntimeError): + """The M3-31 readiness evidence failed closed.""" + + +def canonical_json_fingerprint(value: Any) -> str: + payload = json.dumps( + value, + ensure_ascii=True, + sort_keys=True, + separators=(",", ":"), + ).encode("utf-8") + return hashlib.sha256(payload).hexdigest() + + +def _file_sha256(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as stream: + for chunk in iter(lambda: stream.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def _load_json_object(path: Path) -> dict[str, Any]: + value = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(value, dict): + raise TypeError("JSON document is not an object") + return value + + +def _parse_time(value: Any) -> None: + try: + admission._parse_time(value) + except admission.ChongqingRealSourceAdmissionError as exc: + raise ChongqingAdmissionReadinessError(str(exc)) from exc + + +def _expected_source_binding() -> dict[str, Any]: + return { + "source_id": SOURCE_ID, + "source_group_id": SOURCE_GROUP_ID, + "asset_id": ASSET_ID, + "source_ref": SOURCE_REF, + "upstream_admission_evidence_sha256": UPSTREAM_ADMISSION_EVIDENCE_SHA256, + "upstream_admission_evidence_file_sha256": UPSTREAM_ADMISSION_FILE_SHA256, + "upstream_provenance_evidence_sha256": UPSTREAM_PROVENANCE_EVIDENCE_SHA256, + "upstream_provenance_evidence_file_sha256": UPSTREAM_PROVENANCE_FILE_SHA256, + "upstream_governance_evidence_sha256": UPSTREAM_GOVERNANCE_EVIDENCE_SHA256, + "upstream_governance_evidence_file_sha256": UPSTREAM_GOVERNANCE_FILE_SHA256, + "archive_sha256": admission.EXPECTED_ARCHIVE_SHA256, + "extracted_payload_sha256": admission.EXPECTED_EXTRACTED_PAYLOAD_SHA256, + "source_payload_in_evidence": False, + "absolute_source_paths_in_evidence": False, + } + + +def _requirement_source(key: str) -> str: + if key in REQUIREMENT_KEYS[:6]: + return "derivation" + if key in REQUIREMENT_KEYS[6:14]: + return "governance" + return "protected" + + +def _expected_requirements() -> dict[str, dict[str, Any]]: + return { + key: { + "source": _requirement_source(key), + "status": "missing", + "attestation_sha256": None, + } + for key in REQUIREMENT_KEYS + } + + +def _expected_blockers() -> list[str]: + return [f"admission:{key}_missing" for key in REQUIREMENT_KEYS] + + +def _expected_policy() -> dict[str, bool]: + return { + "metadata_readiness_record_allowed": True, + "admission_requires_complete_derivation": True, + "admission_requires_complete_governance": True, + "admission_requires_fresh_protected_attestation": True, + "source_payload_copy_to_repository_forbidden": True, + "local_profile_is_not_production_admission": True, + "content_admission_authorized": False, + "landing_authority_creation_allowed": False, + "resource_version_creation_allowed": False, + "platform_run_creation_allowed": False, + "scheduler_submission_allowed": False, + "provider_mutation_allowed": False, + } + + +def _expected_claims() -> dict[str, bool]: + return { + "upstream_evidence_bound": True, + "derivation_attestation_complete": False, + "governance_decisions_complete": False, + "fresh_protected_attestation_valid": False, + "admission_eligible": False, + "source_content_admitted": False, + "landing_authority_created": False, + "resource_version_created": False, + "platform_run_created": False, + "scheduler_submission_authorized": False, + "provider_mutation_authorized": False, + "production_ingestion_verified": False, + "production_ready": False, + } + + +def _path_or_payload_findings(value: Any, prefix: str = "") -> list[str]: + findings = admission._sensitive_paths(value, prefix) + rendered = json.dumps(value, ensure_ascii=False, sort_keys=True) + for forbidden in ( + "/Users/", + "/private/", + "Downloads/", + "geometry_values", + "od_rows", + "flow_rows", + "local_source_path", + ): + if forbidden in rendered: + findings.append(f"forbidden:{forbidden}") + return sorted(set(findings)) + + +def validate_evidence(evidence: Mapping[str, Any]) -> list[str]: + errors: list[str] = [] + if set(evidence) != EVIDENCE_INVENTORY: + errors.append("M3-31 evidence inventory does not match") + stable = {key: value for key, value in evidence.items() if key != "evidence_sha256"} + if evidence.get("evidence_sha256") != canonical_json_fingerprint(stable): + errors.append("M3-31 evidence fingerprint does not match") + if evidence.get("schema") != EVIDENCE_SCHEMA or evidence.get("status") != STATUS: + errors.append("M3-31 evidence schema or status does not match") + try: + _parse_time(evidence.get("captured_at")) + except ChongqingAdmissionReadinessError as exc: + errors.append(str(exc)) + + source = evidence.get("source_binding") + if not isinstance(source, Mapping) or set(source) != SOURCE_BINDING_INVENTORY: + errors.append("M3-31 source binding inventory does not match") + source = {} + for key, expected in _expected_source_binding().items(): + if source.get(key) != expected: + errors.append(f"M3-31 source binding does not match: {key}") + for key in ( + "upstream_admission_evidence_sha256", + "upstream_admission_evidence_file_sha256", + "upstream_provenance_evidence_sha256", + "upstream_provenance_evidence_file_sha256", + "upstream_governance_evidence_sha256", + "upstream_governance_evidence_file_sha256", + "archive_sha256", + "extracted_payload_sha256", + ): + if not SHA256_PATTERN.fullmatch(str(source.get(key) or "")): + errors.append(f"M3-31 source fingerprint is invalid: {key}") + if not admission.SOURCE_REF_PATTERN.fullmatch(str(source.get("source_ref") or "")): + errors.append("M3-31 source reference is invalid") + + requirements = evidence.get("required_evidence") + if not isinstance(requirements, Mapping) or set(requirements) != set(REQUIREMENT_KEYS): + errors.append("M3-31 required evidence inventory does not match") + requirements = {} + for key, expected in _expected_requirements().items(): + record = requirements.get(key) + if not isinstance(record, Mapping) or set(record) != REQUIREMENT_RECORD_INVENTORY: + errors.append(f"M3-31 requirement record does not match: {key}") + continue + if dict(record) != expected: + errors.append(f"M3-31 requirement remains unresolved: {key}") + if record.get("status") not in REQUIREMENT_STATUSES: + errors.append(f"M3-31 requirement status is invalid: {key}") + attestation = record.get("attestation_sha256") + if attestation is not None and not SHA256_PATTERN.fullmatch(str(attestation)): + errors.append(f"M3-31 requirement attestation is invalid: {key}") + + if evidence.get("blockers") != _expected_blockers(): + errors.append("M3-31 blocker inventory does not match") + + policy = evidence.get("admission_policy") + if not isinstance(policy, Mapping) or set(policy) != ADMISSION_POLICY_INVENTORY: + errors.append("M3-31 admission policy inventory does not match") + policy = {} + for key, expected in _expected_policy().items(): + if policy.get(key) is not expected: + errors.append(f"M3-31 admission policy does not match: {key}") + + claims = evidence.get("claims") + expected_claims = _expected_claims() + if not isinstance(claims, Mapping) or set(claims) != CLAIMS: + errors.append("M3-31 claims inventory does not match") + else: + for key, expected in expected_claims.items(): + if claims.get(key) is not expected: + errors.append(f"M3-31 claim does not match: {key}") + + if _path_or_payload_findings(evidence): + errors.append("M3-31 evidence contains a path or payload marker") + return sorted(set(errors)) + + +def build_validation_report( + evidence_path: Path = DEFAULT_EVIDENCE_PATH, +) -> dict[str, Any]: + try: + file_sha256 = _file_sha256(evidence_path) + evidence = _load_json_object(evidence_path) + errors = validate_evidence(evidence) + if EVIDENCE_FILE_SHA256 and file_sha256 != EVIDENCE_FILE_SHA256: + errors.append("M3-31 evidence file fingerprint does not match") + except (OSError, TypeError, ValueError, json.JSONDecodeError) as exc: + evidence = {} + file_sha256 = None + errors = [f"M3-31 evidence is unreadable: {type(exc).__name__}"] + claims = evidence.get("claims") + return { + "schema": VALIDATION_SCHEMA, + "status": "valid" if not errors else "invalid", + "errors": sorted(set(errors)), + "evidence_file_sha256": file_sha256, + "evidence_sha256": evidence.get("evidence_sha256"), + "source_id": evidence.get("source_binding", {}).get("source_id") + if isinstance(evidence.get("source_binding"), Mapping) + else None, + "candidate_asset_id": evidence.get("source_binding", {}).get("asset_id") + if isinstance(evidence.get("source_binding"), Mapping) + else None, + "pending_requirement_count": sum( + 1 + for record in evidence.get("required_evidence", {}).values() + if isinstance(record, Mapping) and record.get("status") == "missing" + ) + if isinstance(evidence.get("required_evidence"), Mapping) + else None, + "admission_eligible": ( + claims.get("admission_eligible") if isinstance(claims, Mapping) else None + ), + "source_content_admitted": ( + claims.get("source_content_admitted") if isinstance(claims, Mapping) else None + ), + } + + +def main(argv: list[str] | None = None) -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--evidence", type=Path, default=DEFAULT_EVIDENCE_PATH) + args = parser.parse_args(argv) + try: + report = build_validation_report(args.evidence) + except ChongqingAdmissionReadinessError as exc: + print(f"Chongqing admission readiness: {exc}") + return 1 + print(json.dumps(report, ensure_ascii=False, indent=2, sort_keys=True)) + return 0 if report["status"] == "valid" else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/data_agent/test_chongqing_admission_readiness.py b/data_agent/test_chongqing_admission_readiness.py new file mode 100644 index 00000000..1d81d485 --- /dev/null +++ b/data_agent/test_chongqing_admission_readiness.py @@ -0,0 +1,119 @@ +import json +from copy import deepcopy + +from data_agent import chongqing_admission_readiness as readiness + + +def _evidence() -> dict: + return json.loads(readiness.DEFAULT_EVIDENCE_PATH.read_text(encoding="utf-8")) + + +def _rehash(value: dict) -> None: + stable = {key: item for key, item in value.items() if key != "evidence_sha256"} + value["evidence_sha256"] = readiness.canonical_json_fingerprint(stable) + + +def test_checked_readiness_profile_is_valid_but_blocked(): + evidence = _evidence() + + assert readiness.validate_evidence(evidence) == [] + report = readiness.build_validation_report() + + assert report["status"] == "valid" + assert report["candidate_asset_id"] == "bishan_land_use_dltb_local" + assert report["pending_requirement_count"] == 15 + assert report["admission_eligible"] is False + assert report["source_content_admitted"] is False + + +def test_readiness_binds_all_three_upstream_evidence_layers(): + source = _evidence()["source_binding"] + + assert source["upstream_admission_evidence_sha256"] == ( + "a2196495d845d61be939c7fc36a7f05c3567e365599d2d04be0aab9c568459c1" + ) + assert source["upstream_provenance_evidence_sha256"] == ( + "b56ce0c036827d4338ab2cfae8f3fb4c9e1e78ec18aac243272f1f77801300ef" + ) + assert source["upstream_governance_evidence_sha256"] == ( + "97cf11ab8938c048dce9db903d1a4f30758f208dec6dad1a08b740a4a8fe7b6f" + ) + + +def test_required_evidence_is_partitioned_by_authority_boundary(): + requirements = _evidence()["required_evidence"] + + assert all( + requirements[key]["source"] == "derivation" + for key in readiness.REQUIREMENT_KEYS[:6] + ) + assert all( + requirements[key]["source"] == "governance" + for key in readiness.REQUIREMENT_KEYS[6:14] + ) + assert requirements["fresh_protected_attestation"]["source"] == "protected" + assert all(record["status"] == "missing" for record in requirements.values()) + + +def test_rehash_cannot_turn_readiness_into_admission(): + evidence = deepcopy(_evidence()) + evidence["claims"]["admission_eligible"] = True + evidence["claims"]["source_content_admitted"] = True + _rehash(evidence) + + errors = readiness.validate_evidence(evidence) + + assert "M3-31 claim does not match: admission_eligible" in errors + assert "M3-31 claim does not match: source_content_admitted" in errors + + +def test_requirement_attestation_must_be_a_sha256(): + evidence = deepcopy(_evidence()) + evidence["required_evidence"]["owner_decision"]["attestation_sha256"] = "not-a-sha" + _rehash(evidence) + + errors = readiness.validate_evidence(evidence) + + assert "M3-31 requirement attestation is invalid: owner_decision" in errors + + +def test_upstream_governance_fingerprint_cannot_drift(): + evidence = deepcopy(_evidence()) + evidence["source_binding"]["upstream_governance_evidence_sha256"] = "0" * 64 + _rehash(evidence) + + errors = readiness.validate_evidence(evidence) + + assert "M3-31 source binding does not match: upstream_governance_evidence_sha256" in errors + + +def test_path_or_payload_marker_is_rejected_even_after_rehash(): + evidence = deepcopy(_evidence()) + evidence["required_evidence"]["owner_decision"]["attestation_sha256"] = ( + "/private/approval.json" + ) + _rehash(evidence) + + errors = readiness.validate_evidence(evidence) + + assert "M3-31 evidence contains a path or payload marker" in errors + + +def test_policy_keeps_all_side_effect_authority_false(): + policy = _evidence()["admission_policy"] + + assert policy["metadata_readiness_record_allowed"] is True + assert policy["content_admission_authorized"] is False + assert policy["landing_authority_creation_allowed"] is False + assert policy["resource_version_creation_allowed"] is False + assert policy["platform_run_creation_allowed"] is False + assert policy["scheduler_submission_allowed"] is False + assert policy["provider_mutation_allowed"] is False + + +def test_validator_report_is_path_free(): + report = readiness.build_validation_report() + rendered = json.dumps(report, ensure_ascii=False) + + assert "/private/" not in rendered + assert "/Users/" not in rendered diff --git a/docs/architecture-decisions/adr-078-chongqing-protected-admission-readiness.md b/docs/architecture-decisions/adr-078-chongqing-protected-admission-readiness.md new file mode 100644 index 00000000..6cdd7a85 --- /dev/null +++ b/docs/architecture-decisions/adr-078-chongqing-protected-admission-readiness.md @@ -0,0 +1,76 @@ +# ADR-078: Chongqing protected admission readiness + +**Status**: Accepted + +**Date**: 2026-08-17 + +**Decision owners**: Platform Architecture, Data Platform, Data Governance, Security + +## Context + +M3-28 records the Chongqing source as a metadata-only physical baseline. M3-29 +records the archive-to-working-set comparison without proving derivation. M3-30 +selects the first land-parcel candidate and freezes eight governance decision +slots, all still pending. These records are individually verifiable, but they +do not yet provide one admission gate that can be consumed by a future protected +ingestion workflow. + +Without a unified gate, an ingestion implementation could accidentally treat a +complete-looking checklist as authority while omitting one derivation input, +one governance decision, or the protected environment attestation. + +## Decision + +Adopt M3-31 as an immutable, metadata-only admission readiness contract for +`source://chongqing-planning-institute-sample/assets/bishan_land_use_dltb_local`. +It binds the M3-28 admission evidence, M3-29 provenance evidence and M3-30 +governance evidence by both logical and file fingerprints. + +The contract fixes fifteen required inputs in three authority classes: + +- six derivation inputs: operator identity, tool version, command/workflow + digest, modified-entry manifest, additional-entry manifest, and + archive-to-working-set attestation; +- eight governance decisions: owner, license, retention, access, + privacy/sensitivity, standard version, DataSLO, and golden result; +- one fresh protected admission attestation. + +The checked profile records all fifteen as `missing`, derives +`admission_eligible=false`, and is validated in CI. A future protected workflow +may consume this contract only after it supplies independently attested values +for every requirement and re-evaluates the complete bundle. + +## Authority boundary + +M3-31 is a readiness record, not an admission decision. It creates no Landing +object, ResourceVersion, PlatformRun, scheduler submission, provider mutation, +lakehouse table, serving projection, or production ingestion authority. It does +not copy source payloads and contains no absolute source path, record value, or +geometry. + +Rehashing the JSON cannot promote any claim. `admission_eligible`, +`source_content_admitted`, and `production_ready` remain false until a separate +protected verifier accepts fresh external attestations. + +## Consequences + +**Positive**: the next admission workflow has one explicit, fingerprint-bound +contract and a complete blocker inventory instead of loosely coupled notes. + +**Positive**: CI can reject requirement drift, path/payload leakage and any +attempt to convert a pending readiness profile into write authority. + +**Negative**: AR-2 remains `in_progress`; all fifteen external requirements +remain unresolved and no content may enter Landing. + +## Verification + +```bash +./scripts/chongqing-admission-readiness.sh +python -m pytest data_agent/test_chongqing_admission_readiness.py -q +``` + +The checked evidence fingerprint is +`2f5ae24ab904af0eed18ee7c517ab5c4638cbdf0923c9345b0041af185d25591`; its +file SHA-256 is +`c595065e152988529ff12e2301d59caebb31d2889658a676c9d1f8239e6f8372`. diff --git a/docs/evidence/chongqing-admission-readiness-2026-08-17.json b/docs/evidence/chongqing-admission-readiness-2026-08-17.json new file mode 100644 index 00000000..dcef4878 --- /dev/null +++ b/docs/evidence/chongqing-admission-readiness-2026-08-17.json @@ -0,0 +1,145 @@ +{ + "schema": "gda.chongqing_admission_readiness.v1", + "status": "blocked_pending_protected_admission_attestation", + "captured_at": "2026-08-17T11:00:00Z", + "source_binding": { + "source_id": "chongqing-planning-institute-sample", + "source_group_id": "bishan-planning-materials", + "asset_id": "bishan_land_use_dltb_local", + "source_ref": "source://chongqing-planning-institute-sample/assets/bishan_land_use_dltb_local", + "upstream_admission_evidence_sha256": "a2196495d845d61be939c7fc36a7f05c3567e365599d2d04be0aab9c568459c1", + "upstream_admission_evidence_file_sha256": "9b5c20369c235f7e0a2f2cb0a21cee77f86981aa273bac196605a4803b05ce83", + "upstream_provenance_evidence_sha256": "b56ce0c036827d4338ab2cfae8f3fb4c9e1e78ec18aac243272f1f77801300ef", + "upstream_provenance_evidence_file_sha256": "cfae0478c76452a155e8af42ec8499e4e7876a49c1dbb98648526025cb154360", + "upstream_governance_evidence_sha256": "97cf11ab8938c048dce9db903d1a4f30758f208dec6dad1a08b740a4a8fe7b6f", + "upstream_governance_evidence_file_sha256": "25bc5e2dfc5528f5556e7174f8c99fed7abaf30b9312528f5164c16bdf7cca9a", + "archive_sha256": "2043b60c2f4f7f32a31388a634fae4ac28534990e205aa86b8df0e4b64dcbbca", + "extracted_payload_sha256": "e7e81e4f53f9f174792f500fbfdfde6bee30ec03beac8cbd91771fe09f548ea6", + "source_payload_in_evidence": false, + "absolute_source_paths_in_evidence": false + }, + "required_evidence": { + "operator_identity": { + "source": "derivation", + "status": "missing", + "attestation_sha256": null + }, + "tool_version": { + "source": "derivation", + "status": "missing", + "attestation_sha256": null + }, + "command_digest": { + "source": "derivation", + "status": "missing", + "attestation_sha256": null + }, + "modified_entry_manifest": { + "source": "derivation", + "status": "missing", + "attestation_sha256": null + }, + "additional_entry_manifest": { + "source": "derivation", + "status": "missing", + "attestation_sha256": null + }, + "archive_to_working_set_attestation": { + "source": "derivation", + "status": "missing", + "attestation_sha256": null + }, + "owner_decision": { + "source": "governance", + "status": "missing", + "attestation_sha256": null + }, + "license_decision": { + "source": "governance", + "status": "missing", + "attestation_sha256": null + }, + "retention_decision": { + "source": "governance", + "status": "missing", + "attestation_sha256": null + }, + "access_decision": { + "source": "governance", + "status": "missing", + "attestation_sha256": null + }, + "privacy_sensitivity_decision": { + "source": "governance", + "status": "missing", + "attestation_sha256": null + }, + "standard_version_decision": { + "source": "governance", + "status": "missing", + "attestation_sha256": null + }, + "data_slo_decision": { + "source": "governance", + "status": "missing", + "attestation_sha256": null + }, + "golden_result_decision": { + "source": "governance", + "status": "missing", + "attestation_sha256": null + }, + "fresh_protected_attestation": { + "source": "protected", + "status": "missing", + "attestation_sha256": null + } + }, + "blockers": [ + "admission:operator_identity_missing", + "admission:tool_version_missing", + "admission:command_digest_missing", + "admission:modified_entry_manifest_missing", + "admission:additional_entry_manifest_missing", + "admission:archive_to_working_set_attestation_missing", + "admission:owner_decision_missing", + "admission:license_decision_missing", + "admission:retention_decision_missing", + "admission:access_decision_missing", + "admission:privacy_sensitivity_decision_missing", + "admission:standard_version_decision_missing", + "admission:data_slo_decision_missing", + "admission:golden_result_decision_missing", + "admission:fresh_protected_attestation_missing" + ], + "admission_policy": { + "metadata_readiness_record_allowed": true, + "admission_requires_complete_derivation": true, + "admission_requires_complete_governance": true, + "admission_requires_fresh_protected_attestation": true, + "source_payload_copy_to_repository_forbidden": true, + "local_profile_is_not_production_admission": true, + "content_admission_authorized": false, + "landing_authority_creation_allowed": false, + "resource_version_creation_allowed": false, + "platform_run_creation_allowed": false, + "scheduler_submission_allowed": false, + "provider_mutation_allowed": false + }, + "claims": { + "upstream_evidence_bound": true, + "derivation_attestation_complete": false, + "governance_decisions_complete": false, + "fresh_protected_attestation_valid": false, + "admission_eligible": false, + "source_content_admitted": false, + "landing_authority_created": false, + "resource_version_created": false, + "platform_run_created": false, + "scheduler_submission_authorized": false, + "provider_mutation_authorized": false, + "production_ingestion_verified": false, + "production_ready": false + }, + "evidence_sha256": "2f5ae24ab904af0eed18ee7c517ab5c4638cbdf0923c9345b0041af185d25591" +} diff --git a/docs/roadmap.md b/docs/roadmap.md index 0577994d..3533e98f 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -442,6 +442,7 @@ AR-0 Architecture / Schema / Runtime Truth - 真实源 admission contract:以不可变 archive checksum、解压 payload fingerprint、source-group manifest、metadata profile 和治理 blocker 建立准入基线;证据不得含源 payload、绝对路径、记录值或 geometry,profiling 不等于 content admission。 - M3-29 extraction provenance contract:以 M3-28 上游 evidence fingerprint、archive/extracted comparison 和明确的 derivation blocker 固定“已观察比较”与“派生证明缺失”的边界;operator、tool、command、modified/additional manifest 和 archive-to-working-set attestation 未齐全前不得 content admission。 - M3-30 source governance gate:选择 `bishan_land_use_dltb_local` 作为首条地类图斑候选,绑定 M3-28/M3-29 指纹并把 owner、license、retention、access、privacy/sensitivity、标准版本、DataSLO、golden result 八项决策冻结为独立 pending records;完整派生证明、签名决策和 fresh protected attestation 未齐全前不得 content admission。 +- M3-31 protected admission readiness contract:绑定 M3-28/M3-29/M3-30 的逻辑与文件 fingerprints,把 6 项 derivation、8 项 governance 和 1 项 protected attestation 固定为 15 个 fail-closed requirements;`admission_eligible=false` 前不得创建 Landing、ResourceVersion、PlatformRun、scheduler submission 或 provider mutation。 - SourceDefinition、CredentialReference、SourceCapability、SyncDefinition/Version、SyncRun、Cursor/Watermark、SchemaDriftEvent 和 Reconciliation。 - 数据库、对象存储/空间文件、HTTP/STAC 三类代表 source 的连接、凭据、连通、发现、preview、profile 和 owner 登记。 - 全量/增量微批的 Append/Overwrite/Merge 策略,以及至少一个真实 CDC 或事件流 source 通过 Flink 写入版本化 Bronze;覆盖 watermark/offset、checkpoint、迟到/乱序、源端删除、幂等、对账、重放和失败恢复。 @@ -689,7 +690,7 @@ Golden checks 至少覆盖: 3. 冻结 ResourceURN、ResourceVersion、PlatformDefinition/PlatformRun/FrameworkAttemptObservation/Artifact/LineageEvent、SubjectContext 与 storage/table/compute provider 最小合同。 4. 分阶段实现 `gda-metadata-fabric-bridge`:M1 只读 mapping/reconciliation、M2a 本地 foundation/重启连续性、M2b-1 本地三存储恢复、M2b-2 隔离 versioned/Object-Locked repository round-trip、M2b-3 本机双集群 + Kubernetes 外 COMPLIANCE repository + 独立 writer/reader、M2c-1 provider-native metrics、M2c-2 临时 OTel Collector + JSON Exporter 的双周期本地 pipeline、M2c-3 本地单 job scrape 故障检测/配置恢复/完整清理 evidence、M2c-4 绑定 source revision 的 production observability readiness contract,以及 M2d-1 本地 kindnet 跨节点 NetworkPolicy enforcement 已验证;M2c-4 当前仍有 20 项 blockers,M2d-1 也未验证生产 provider policy 或 tenant isolation。下一步完成 source host/cluster 外的生产 bucket、KMS/TLS/workload identity、source-loss recovery 与 RPO/RTO,并批准 metrics backend、retention、OTel/TLS、tenant、alert/SLO/owner 后在受保护环境验证持续采集、存储、查询、真实告警投递、runbook 响应和 provider NetworkPolicy;再推进 OIDC、upgrade/rollback、registry provenance 和 owner/runbook;之后才进入 M3 ingestion/OpenLineage/conformance。 5. 实现 `gda-orchestration-gateway`、DolphinScheduler process/task/schedule/complement/worker-group、Spark/Flink provider task adapter 和故障注入;不再开发新的 lease/queue/scheduler。 -6. M3-28 已冻结全量重庆真实源的 path-free physical/metadata admission baseline,M3-29 已建立 metadata-only extraction provenance gap baseline,M3-30 已选择 `bishan_land_use_dltb_local` 并把八项治理输入固化为 fail-closed pending records;下一步取得 operator/tool/command、modified/additional manifest、archive-to-working-set attestation、八项签名治理决策与 fresh protected attestation,未获批准前不得 content admission。 +6. M3-28 已冻结全量重庆真实源的 path-free physical/metadata admission baseline,M3-29 已建立 metadata-only extraction provenance gap baseline,M3-30 已选择 `bishan_land_use_dltb_local` 并把八项治理输入固化为 fail-closed pending records,M3-31 已将三层证据统一成 protected admission readiness contract;下一步取得 15 项外部 attestation 并由受保护 verifier 重新评估,未获批准前不得 content admission。 7. 冻结 Default Lakehouse、Cloud Managed、Lightweight Integrated profiles;以统一 Run 完成默认 MinIO/Iceberg/Spark/Flink、轻量 PostGIS/DuckDB 和 Azure 代表 adapter 的 conformance smoke。 8. 实现跨 profile 的 Raw -> ODS -> DIM/DWD -> DWS -> ADS 通用生产、质量、发布、回滚和 golden equivalence。 9. 建立 DataProductBlueprint、模型版本和 Visual/SQL/Notebook 共用 definition 的 Build 工作台,打通 preview、test、publish、approval 和 rollback。 @@ -730,7 +731,7 @@ AR-4 parity/control gate 退出前暂停以下主线扩张: |---|---|---| | AR-0 Architecture/Schema/Runtime Truth Freeze | `in_progress` | 全环境 schema/config fingerprint、迁移 fail-closed、事实清单、storage/compute/GIS serving provider profile/capability、ADR-017 benchmark、owner/SLO 和首条数据/服务验收集冻结 | | AR-1 Unified Metadata + Orchestration Control Planes | `in_progress` | controlled gateway、DolphinScheduler adapter、Metadata Fabric M1/M2a/M2b、M2c-1 provider metrics、M2c-2 本地临时 OTel pipeline、M2c-3 本地 scrape failure/recovery、M2c-4 production observability readiness contract 与 M2d-1 本地跨节点 NetworkPolicy enforcement 已验证,生产观测和生产 policy/tenant isolation 仍 blocked;下一证据是 source host/cluster 外的生产 recovery、持久 metrics backend/TLS/tenant/真实 alert delivery/SLO、OIDC、受保护 provider NetworkPolicy、升级回滚/registry provenance,以及受控 ingestion/replay 与无双写验收 | -| AR-2 Source/Ingestion + Geospatial Lakehouse Vertical Slice | `in_progress` | M3-28 admission baseline、M3-29 extraction provenance gap baseline 与 M3-30 first-candidate governance gate 已完成;下一证据是 operator/tool/command、modified/additional manifest、archive-to-working-set attestation、八项签名治理决策与 fresh protected attestation,随后才可授权 immutable Landing authority、content admission 和 ingestion;最终仍需三类代表源、`DriveTransfer` 云盘客户端、大文件恢复、默认湖仓、轻量存算一体及 Azure 代表 adapter 通过 provider conformance 与 Raw -> ADS 验收 | +| AR-2 Source/Ingestion + Geospatial Lakehouse Vertical Slice | `in_progress` | M3-28 admission baseline、M3-29 extraction provenance gap baseline、M3-30 first-candidate governance gate 与 M3-31 protected admission readiness contract 已完成;下一证据是 15 项外部 attestation、受保护 verifier 复核,随后才可授权 immutable Landing authority、content admission 和 ingestion;最终仍需三类代表源、`DriveTransfer` 云盘客户端、大文件恢复、默认湖仓、轻量存算一体及 Azure 代表 adapter 通过 provider conformance 与 Raw -> ADS 验收 | | AR-3 Data Product Engineering + Governance Workbench | `planned` | Blueprint、模型、Visual/SQL/Notebook、DataOps CI/CD、质量/安全/审批共用 definition 和产品生命周期 | | AR-4 Asset/GIS Service/Spatial Experience Operations | `planned` | Service Control Plane、Features/Tiles/MVT/COG/STAC/export 及条件 legacy OGC/3D/EDR provider、Gateway/权限/缓存、原子切换/回滚、Discover/Operate/Govern 和无 LLM 多入口通过 conformance/parity/control gate | | AR-5 AgentOps Runtime + UX Uplift | `planned` | DataOps parity/control 通过;Agent bundle eval、deployment、online observation、incident/rollback 和 uplift gate | diff --git a/docs/system-of-record-matrix-2026-07-24.md b/docs/system-of-record-matrix-2026-07-24.md index 9bcee9ec..853ce420 100644 --- a/docs/system-of-record-matrix-2026-07-24.md +++ b/docs/system-of-record-matrix-2026-07-24.md @@ -2,7 +2,7 @@ 日期:2026-08-17 -阶段:AR-0 `in_progress`;AR-1 gateway、成功终局 evidence gate、DolphinScheduler adapter sandbox POC、Metadata Fabric M1/M2、M2c-4/M2d-2 production readiness contracts、M3-1/M3-2、M3-3 local binding ledger、M3-4 local OpenLineage wire delivery、M3-5 local OpenMetadata bounded identity、M3-6 local Gravitino Basic bounded identity 与 M3-7 production identity readiness contract 已验证;AR-2 M3-28 重庆真实源 metadata-only admission baseline、M3-29 extraction provenance gap baseline 与 M3-30 first-candidate governance gate 已建立,内容准入、Landing authority、生产 provider ingestion、生产观测、生产 policy/tenant isolation、生产 identity attestation 和生产切换仍 `in_progress` +阶段:AR-0 `in_progress`;AR-1 gateway、成功终局 evidence gate、DolphinScheduler adapter sandbox POC、Metadata Fabric M1/M2、M2c-4/M2d-2 production readiness contracts、M3-1/M3-2、M3-3 local binding ledger、M3-4 local OpenLineage wire delivery、M3-5 local OpenMetadata bounded identity、M3-6 local Gravitino Basic bounded identity 与 M3-7 production identity readiness contract 已验证;AR-2 M3-28 重庆真实源 metadata-only admission baseline、M3-29 extraction provenance gap baseline、M3-30 first-candidate governance gate 与 M3-31 protected admission readiness contract 已建立,内容准入、Landing authority、生产 provider ingestion、生产观测、生产 policy/tenant isolation、生产 identity attestation 和生产切换仍 `in_progress` 适用分支:`main` @@ -21,7 +21,7 @@ | 部署配置策略 | Compose/K8s/进程环境;`platform_truth.CONFIG_SPECS` 定义关键类型与策略;DolphinScheduler worker 有默认零副本、外部 ConfigMap/Secret 驱动的 Kustomize 模板、静态 validator、staging activation preflight 和受保护的单副本 activation admission/workflow | `.env` 仅补默认;脱敏 snapshot、Secret key attestation、未扩容 Deployment、`ready_for_activation` 和未执行的 activation workflow 都是观测/变更能力 | 版本化 DeploymentProfile + secret reference;部署环境始终优先;模板、preflight 或 admission 通过都不等于环境已启用 | Platform/SRE/Security | AR-0,部分实现;worker 激活合同已验证、真实运行待审批 | | 环境发布与晋级 | publisher `31862363442`、protected verifier `31862984294` 与 staging deploy/observe `31863077257` 已将 `main@5fffc85`、GHCR digest、attested release、cluster/namespace identity 和 live revision 绑定;schema/config/runtime/health/rollout 通过,golden slice 缺失使 promotion fail closed | 旧 mainline、feature branch、CI artifact、JSON、离线 report、单独的 staging deployment 或人工批准都不能成为 production 发布权威 | 由受保护 environment 的 DeploymentRevision 绑定 OCI、provenance artifact、release manifest、golden slice 与全部 live verdict | Platform/SRE/Security/Repository Owner | AR-1 真实 staging 已部署 -> golden slice/production exit gates 待完成 | | 后台运行时清单 | `platform_truth.RUNTIME_INVENTORY` 是代码层登记;`gda_control` 已有受控 PlatformRun 写入口;DolphinScheduler adapter、tenant-scoped managed command worker 与受保护的单副本激活边界已有代码和测试,但 worker 尚未在 staging 运行;M2b recovery runner、M2c-1 provider probe、M2c-2 `_OtelPortForward`、M2c-3 failure rehearsal 与 M2d-1 NetworkPolicy rehearsal 均登记为 `local_verification_only`,不是 scheduler、worker、持续监控、生产 policy controller 或状态权威 | AST primitive report、worker status JSON、FrameworkAttemptObservation、DolphinScheduler instance state、本地 recovery/metrics/network-policy evidence | PlatformRun ledger 唯一登记最终状态;framework/provider attempt 只能回报观测;worker status 仅为进程健康投影;本地演练进程与 evidence 不得变成生产控制器、监控后端或 tenant-isolation 权威 | Platform Architecture | AR-1 adapter/worker/activation 合同已验证 -> staging 运行待审批;metadata recovery/metrics/policy runner 仅本地验证 | -| 原始文件/对象 | M3-28 已以 path-free metadata manifest 记录重庆真实源 archive/extracted fingerprints、source groups、asset profiles 与治理 blockers;M3-29 进一步记录 526 exact、6 modified、0 missing、52 additional 的比较及 6 项派生证明缺口;M3-30 已选择首条地类图斑候选并把八项治理决策固定为 pending records;`source_content_admitted=false`,因此尚无内容写入权威 | admission/provenance/governance evidence JSON、archive/extracted comparison、临时上传、下载缓存、预览文件 | 在 operator/tool/command、modified/additional manifest、archive-to-working-set attestation、八项签名治理决策与 fresh protected attestation 进入正式 authority 后,才可由 immutable Landing object URI + checksum + retention 建立内容权威;本地 scratch 与 checked evidence 均不可替代 Landing | Data Platform | AR-2 `in_progress` | +| 原始文件/对象 | M3-28 已以 path-free metadata manifest 记录重庆真实源 archive/extracted fingerprints、source groups、asset profiles 与治理 blockers;M3-29 进一步记录 526 exact、6 modified、0 missing、52 additional 的比较及 6 项派生证明缺口;M3-30 已选择首条地类图斑候选并把八项治理决策固定为 pending records;M3-31 将上述三层证据绑定为 15 项 pending admission requirements;`source_content_admitted=false`,因此尚无内容写入权威 | admission/provenance/governance/readiness evidence JSON、archive/extracted comparison、临时上传、下载缓存、预览文件 | 在 15 项外部 attestation 经受保护 verifier 复核并进入正式 authority 后,才可由 immutable Landing object URI + checksum + retention 建立内容权威;本地 scratch 与 checked evidence 均不可替代 Landing | Data Platform | AR-2 `in_progress` | | 湖仓表与 snapshot | Iceberg/STAC/S3A 有局部实现,尚无通用发布权威 | STAC item、GeoParquet export | Iceberg catalog snapshot 是分析表版本权威;对象是物理内容,STAC 是发现投影 | Data Platform | AR-2 | | 在线空间数据 | PostGIS 业务表是当前编辑/查询事实,部分临时表混入 | Martin MVT、API JSON、导出文件 | 已批准 DataProductVersion 物化到 PostGIS;不能由瓦片或临时表反向定义产品版本 | GIS/Data Platform | AR-2 -> AR-4 | | 数据资产身份与版本 | `gda_control.resource/resource_version` 已实现 identity、hash、predecessor、tenant FK 和幂等 gateway 写入;`agent_data_assets`、`agent_asset_versions` 仍是兼容写路径 | UI catalog、search index、STAC | GDA ledger 管身份与版本绑定;旧行只有在 tenant、authority identity、checksum 和 version evidence 完整时才可形成 eligible plan;OpenMetadata 管治理目录,Gravitino 管技术对象映射 | Metadata Platform | AR-1 gateway 已验证 -> 生产切换待验收 | @@ -65,6 +65,7 @@ 19. M3-28 只允许 path-free、metadata-only 的重庆真实源 admission;archive/extracted fingerprint、source-group manifest、asset profile 和 57 个治理 blocker 形成准入观察,不形成 Landing object、ResourceVersion、PlatformRun、授权 Artifact、scheduler submission、provider mutation、content admission 或 production ingestion 权威;checked evidence 不得被编辑成批准。 20. M3-29 只允许记录 M3-28 上游 fingerprint 和 archive/extracted comparison;`comparison_observed=true`、`derivation_provenance_complete=false`,operator/tool/command、modified/additional manifest 和 archive-to-working-set attestation 缺失时,不形成 Landing object、ResourceVersion、PlatformRun、scheduler submission、provider mutation、content admission 或 production ingestion 权威;provenance evidence 不得被 rehash 成批准。 21. M3-30 只允许选择 `bishan_land_use_dltb_local` 作为首条候选并记录 owner、license、retention、access、privacy/sensitivity、标准版本、DataSLO、golden result 八项 pending decisions;`candidate_scope_selected=true` 不等于 `source_governance_approved`,checked governance evidence 不形成 Landing object、ResourceVersion、PlatformRun、scheduler submission、provider mutation、content admission 或 production ingestion 权威,也不得被 rehash 成批准。 +22. M3-31 只允许绑定 M3-28/M3-29/M3-30 并记录 6 项 derivation、8 项 governance 与 1 项 protected attestation 的 pending requirements;`admission_eligible=false` 时不形成 Landing object、ResourceVersion、PlatformRun、scheduler submission、provider mutation、content admission 或 production ingestion 权威,readiness evidence 不得被 rehash 成批准。 ## 已建立的 AR-0/AR-1 entry 证据 @@ -99,6 +100,7 @@ - AR-2 M3-28 已完成重庆真实源 metadata-only admission baseline:evidence SHA 为 `a2196495d845d61be939c7fc36a7f05c3567e365599d2d04be0aab9c568459c1`,evidence file SHA 为 `9b5c20369c235f7e0a2f2cb0a21cee77f86981aa273bac196605a4803b05ce83`;archive SHA 为 `2043b60c2f4f7f32a31388a634fae4ac28534990e205aa86b8df0e4b64dcbbca`,extracted payload SHA 为 `e7e81e4f53f9f174792f500fbfdfde6bee30ec03beac8cbd91771fe09f548ea6`;11 个 source groups、16 个 asset profiles、57 个 admission blockers、584 个 extracted files,archive/extracted comparison 为 526 exact、6 modified、0 missing、52 additional;`source_content_admitted=false`。该证据不含 source payload、绝对路径、记录值或 geometry,也不证明 Landing authority、ResourceVersion、Run、授权 Artifact、scheduler/provider mutation、生产 ingestion 或 `production_ready`。 - AR-2 M3-29 已建立重庆 extraction provenance gap baseline:evidence SHA 为 `b56ce0c036827d4338ab2cfae8f3fb4c9e1e78ec18aac243272f1f77801300ef`,evidence file SHA 为 `cfae0478c76452a155e8af42ec8499e4e7876a49c1dbb98648526025cb154360`;上游 M3-28 evidence/file SHA 分别为 `a2196495d845d61be939c7fc36a7f05c3567e365599d2d04be0aab9c568459c1` / `9b5c20369c235f7e0a2f2cb0a21cee77f86981aa273bac196605a4803b05ce83`;比较固定为 526 exact、6 modified、0 missing、52 additional,6 项 derivation evidence 缺失,`comparison_observed=true`、`derivation_provenance_complete=false`、`source_content_admitted=false`、`production_ready=false`。该证据只记录比较关系,不含 source payload、绝对路径、记录值或 geometry,也不证明 archive-to-working-set attestation、Landing authority、ResourceVersion、Run、scheduler/provider mutation 或生产 ingestion。 - AR-2 M3-30 已建立首条重庆地类图斑 source governance gate:候选为 `bishan_land_use_dltb_local`,evidence SHA 为 `97cf11ab8938c048dce9db903d1a4f30758f208dec6dad1a08b740a4a8fe7b6f`,evidence file SHA 为 `25bc5e2dfc5528f5556e7174f8c99fed7abaf30b9312528f5164c16bdf7cca9a`;证据绑定 M3-28/M3-29 fingerprints,八项治理 decision records 全部为 `pending`,另保留 derivation 与 fresh protected attestation blockers;`candidate_scope_selected=true`、`source_governance_approved=false`、`source_content_admitted=false`、`production_ready=false`。该证据不含 source payload、绝对路径、记录值或 geometry,也不证明任何治理批准、Landing authority、ResourceVersion、Run、scheduler/provider mutation 或生产 ingestion。 +- AR-2 M3-31 已建立 protected admission readiness contract:候选为 `bishan_land_use_dltb_local`,evidence SHA 为 `2f5ae24ab904af0eed18ee7c517ab5c4638cbdf0923c9345b0041af185d25591`,evidence file SHA 为 `c595065e152988529ff12e2301d59caebb31d2889658a676c9d1f8239e6f8372`;证据绑定 M3-28/M3-29/M3-30 的逻辑与文件 fingerprints,15 项要求全部为 `missing`,`admission_eligible=false`、`source_content_admitted=false`、`production_ready=false`。该证据只定义未来受保护 verifier 的输入,不含 source payload、绝对路径、记录值或 geometry,也不证明任何 Landing authority、ResourceVersion、Run、scheduler/provider mutation 或生产 ingestion。 ## 下一验收证据 @@ -107,5 +109,5 @@ - 为受保护 activation 提供真实 ConfigMap snapshot、Secret key attestation、provider identity 和 reviewer approval,随后完成 managed outbox worker/provider callback 单副本实际部署、唯一 worker ID、status/lease 故障恢复和无双写证据; - 首条真实图斑链对 golden slice 的 output hash、独立质量结果/evidence、血缘、发布 revision 和 rollback 演练; - OpenMetadata/Gravitino 的 source host/cluster 外生产 backup account/bucket、KMS/TLS/workload identity、PITR/source-loss recovery、RPO/RTO、OIDC、受保护环境 provider NetworkPolicy/tenant isolation、upgrade/rollback、registry provenance、持续 metrics backend/retention/query、真实 alert delivery/SLO owner/runbook,以及受保护 PolicyDecision/Approval、双 provider 最小权限 ingestion、生产持久 binding、受保护 production OpenLineage receiver、无双写 read-back 和 conformance;M1 fixture、M2 本地 evidence/readiness contracts、M3-1 projection candidate、M3-2 local replay、M3-3 临时 binding ledger、M3-4 loopback delivery、M3-5/M3-6 本地临时 provider identity 与 M3-7 pending profile/合成 attestation 均不计入生产退出门; -- M3-29/M3-30 的下一证据必须补齐 operator/tool/command、modified/additional manifest、archive-to-working-set attestation、owner/license/retention/access/privacy-sensitivity/standard-version/DataSLO/golden-result 八项签名决策与 fresh protected attestation;metadata-only admission/provenance/governance evidence 是不可变检查结果,不能被编辑或重解释为内容批准、Landing authority 或 ingestion authorization; +- M3-29/M3-30/M3-31 的下一证据必须补齐 operator/tool/command、modified/additional manifest、archive-to-working-set attestation、owner/license/retention/access/privacy-sensitivity/standard-version/DataSLO/golden-result 八项签名决策与 fresh protected attestation,并由受保护 verifier 重新计算 admission eligibility;metadata-only admission/provenance/governance/readiness evidence 是不可变检查结果,不能被编辑或重解释为内容批准、Landing authority 或 ingestion authorization; - DolphinScheduler/Temporal sandbox 的独立数据库、备份恢复、身份、版本和升级责任证明;DolphinScheduler standalone/H2 不计入此退出门。 diff --git a/scripts/chongqing-admission-readiness.sh b/scripts/chongqing-admission-readiness.sh new file mode 100755 index 00000000..a13a5387 --- /dev/null +++ b/scripts/chongqing-admission-readiness.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +set -euo pipefail + +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +common_git_dir="$(git -C "$repo_root" rev-parse --path-format=absolute --git-common-dir 2>/dev/null || true)" +shared_root="" +if [ -n "$common_git_dir" ]; then + shared_root="$(cd "$common_git_dir/.." && pwd)" +fi + +if [ -n "${PYTHON:-}" ]; then + : +elif [ -x "$repo_root/.venv/bin/python" ]; then + PYTHON="$repo_root/.venv/bin/python" +elif [ -n "$shared_root" ] && [ -x "$shared_root/.venv/bin/python" ]; then + PYTHON="$shared_root/.venv/bin/python" +else + PYTHON="python" +fi + +cd "$repo_root" +exec "$PYTHON" -m data_agent.chongqing_admission_readiness "$@"