feat(s3): align harness logic contracts
This commit is contained in:
parent
e90bf2d692
commit
006f65ec9b
157
docs/memorys/2026-06-04-S3Task1合同门禁.md
Normal file
157
docs/memorys/2026-06-04-S3Task1合同门禁.md
Normal file
@ -0,0 +1,157 @@
|
||||
# 2026-06-04 S3 Task1 合同门禁
|
||||
|
||||
## 结论
|
||||
|
||||
S3 Task1 `Align S0 Harness GameLogicModule And ValidationReport Contracts` 已完成实现、controller 自测,并通过 fresh review gate:
|
||||
|
||||
- spec compliance review:PASS,无 Critical / Important。
|
||||
- quality / feasibility review:PASS,无 Critical / Important。
|
||||
|
||||
可以提交 Task1,并在提交后进入 S3 Task2:`Define GameConfig, GameLogicModule, And RuntimeSdkContract Shared Contracts`。
|
||||
|
||||
## 变更范围
|
||||
|
||||
- `harness/schemas/game-logic-module.schema.json`
|
||||
- `harness/schemas/validation-report.schema.json`
|
||||
- `harness/scripts/validate-harness.mjs`
|
||||
- `harness/fixtures/mvp/valid/simulation-game-logic-module-valid.json`
|
||||
- `harness/fixtures/mvp/valid/game-logic-validation-report-valid.json`
|
||||
- `harness/fixtures/mvp/invalid/**` 中 S3 GameLogicModule / ValidationReport 相关 fixtures。
|
||||
|
||||
未创建 shared-contracts TS 文件、API modules、business routes/UI、runtime、conversion、feed 或 deploy 实现。
|
||||
|
||||
## 子代理与 Review Gate
|
||||
|
||||
| Role | Agent ID | Result | Blocking findings |
|
||||
| --- | --- | --- | --- |
|
||||
| implementer | `019e9218-daa7-7860-b10a-7e157edf221d` | DONE | 无 |
|
||||
| spec compliance review | `019e9226-8a98-7e42-ab7e-923aa405d871` | PASS | 无 Critical / Important |
|
||||
| quality / feasibility review | `019e9226-cc72-7983-85bc-cdd51cf7ca10` | PASS | 无 Critical / Important |
|
||||
|
||||
## 实现摘要
|
||||
|
||||
- `GameLogicModule` schema 升级为 S3 descriptor 形态:
|
||||
- `sourceRef` 改为结构化对象:`gameConfigId`、`templateVersion`、`buildInputPath`。
|
||||
- 新增必需字段:`exportSignature`、`runtimeSdkContractVersion`、`buildGraphId`。
|
||||
- `validationStatus` 加入 `forbiddenProperties`,descriptor 自报 gate 状态会被拒绝。
|
||||
- `ValidationReport` schema 升级为 S3 validator output 形态:
|
||||
- `validatorVersion` 必填。
|
||||
- valid schema 只接受 `result="passed"`。
|
||||
- `additionalProperties=false` 继续 fail closed。
|
||||
- `validate-harness.mjs` 仅补充 Task1 所需 harness 逻辑:
|
||||
- 新增 GameLogicModule / ValidationReport fixture target 映射。
|
||||
- 新增 `importGraph` 必须被 `allowedImports` 覆盖的跨字段校验。
|
||||
- 新增 descriptor 自报 `validationStatus` 的稳定 reason:`GAME_LOGIC_VALIDATION_STATUS_SELF_REPORTED`。
|
||||
- 更新 invalid fixture 总数:`invalidFixtures=60`、`mvpInvalidFixtures=39`。
|
||||
- 新增或升级 invalid fixtures 覆盖:
|
||||
- missing `exportSignature`
|
||||
- missing `runtimeSdkContractVersion`
|
||||
- missing `buildGraphId`
|
||||
- string `sourceRef` old shape
|
||||
- descriptor self-reported `validationStatus`
|
||||
- `ValidationReport result=failed`
|
||||
- `ValidationReport` checksum mismatch
|
||||
- `ValidationReport importGraph` using disallowed import
|
||||
- missing `targetContract`
|
||||
- missing `validatorVersion`
|
||||
- missing `createdAt`
|
||||
- `ValidationReport` extra properties
|
||||
|
||||
## Controller 自测
|
||||
|
||||
已运行并通过:
|
||||
|
||||
```bash
|
||||
node --check harness/scripts/validate-harness.mjs
|
||||
node harness/scripts/validate-harness.mjs
|
||||
node harness/scripts/validate-harness.mjs --check-invalid
|
||||
node harness/scripts/validate-harness.mjs --contract GameLogicModule --input harness/fixtures/mvp/valid/simulation-game-logic-module-valid.json
|
||||
node harness/scripts/validate-harness.mjs --contract ValidationReport --input harness/fixtures/mvp/valid/game-logic-validation-report-valid.json
|
||||
pnpm check:s3-scope
|
||||
git diff --check
|
||||
```
|
||||
|
||||
关键输出:
|
||||
|
||||
```text
|
||||
PASS harness validation
|
||||
validFixtures=45
|
||||
invalidFixtures=60
|
||||
foundationValidFixtures=21
|
||||
mvpValidFixtures=24
|
||||
foundationInvalidFixtures=21
|
||||
mvpInvalidFixtures=39
|
||||
invalidCoverage=60/60
|
||||
S3 scope check passed.
|
||||
```
|
||||
|
||||
## Review Evidence
|
||||
|
||||
Spec reviewer 额外运行并确认:
|
||||
|
||||
```text
|
||||
jq .
|
||||
node --check harness/scripts/validate-harness.mjs
|
||||
node harness/scripts/validate-harness.mjs --check-invalid
|
||||
node harness/scripts/validate-harness.mjs --contract GameLogicModule --input harness/fixtures/mvp/valid/simulation-game-logic-module-valid.json
|
||||
node harness/scripts/validate-harness.mjs --contract ValidationReport --input harness/fixtures/mvp/valid/game-logic-validation-report-valid.json
|
||||
git diff --check -- harness/...
|
||||
pnpm check:s3-scope
|
||||
```
|
||||
|
||||
Quality reviewer 额外确认:
|
||||
|
||||
```text
|
||||
full harness PASS
|
||||
invalidCoverage=60/60
|
||||
新增 untracked fixtures 已被加载并计入 mvpInvalidFixtures=39
|
||||
ValidationReport.result passed-only valid shape 未破坏 --check-invalid fail-closed 路径
|
||||
```
|
||||
|
||||
## SHA-256
|
||||
|
||||
| Path | SHA-256 |
|
||||
| --- | --- |
|
||||
| `harness/schemas/game-logic-module.schema.json` | `d88db45e2aa76c12ecd257240d66b4306e04c34cbeeb28000acb1ebc93400d06` |
|
||||
| `harness/schemas/validation-report.schema.json` | `92fb4f979cd33efc808e5d84533ad578ac9244f26ab1fe27374bfdeaeb9a39b1` |
|
||||
| `harness/scripts/validate-harness.mjs` | `16bfcfe662b34d5d13cc9ab1c604ee47cbb9bbc18e7517822a85583c200850e1` |
|
||||
| `harness/fixtures/mvp/valid/simulation-game-logic-module-valid.json` | `5760d2593f0162af3e6b0b63dfb01d369e6c187e3b7ce42b04a0b1ce355beca2` |
|
||||
| `harness/fixtures/mvp/valid/game-logic-validation-report-valid.json` | `3016a98ab390fe517f7d39315f9231da49a23b849619a18e047411e176825a1e` |
|
||||
| `docs/superpowers/plans/2026-05-31-mvp-S3-simulation-slice.md` | `cbc7fb2afaab3cf64b578984b9def7ef265a46aa07029b732ac4ecbc2d68b76a` |
|
||||
| `docs/superpowers/specs/2026-05-31-mvp-S3-simulation-slice-design.md` | `08abff96b9a1a7b7680e71678f5c479edab24467dd10185c0b0b0e29ea7e613e` |
|
||||
| `docs/superpowers/plans/2026-06-01-implementation-review-gate-protocol.md` | `c8726acc5457ab87fb8f26c2e439e1d1b98df0255af78e4d6e8d9636dc518478` |
|
||||
|
||||
## Git Status Snapshot
|
||||
|
||||
写入本文档前 `git status --short --branch --untracked-files=all`:
|
||||
|
||||
```text
|
||||
## codex/s1-task9-s2-prep...origin/codex/s1-task9-s2-prep [ahead 2]
|
||||
M harness/fixtures/mvp/invalid/dom-bom-usage-invalid.json
|
||||
M harness/fixtures/mvp/invalid/dynamic-execution-invalid.json
|
||||
M harness/fixtures/mvp/invalid/game-logic-validated-without-report-invalid.json
|
||||
M harness/fixtures/mvp/invalid/game-logic-validation-report-failed-invalid.json
|
||||
M harness/fixtures/mvp/invalid/game-logic-validation-report-web-only-sdk-invalid.json
|
||||
M harness/fixtures/mvp/invalid/node-api-usage-invalid.json
|
||||
M harness/fixtures/mvp/invalid/validation-report-checksum-mismatch-invalid.json
|
||||
M harness/fixtures/mvp/valid/game-logic-validation-report-valid.json
|
||||
M harness/fixtures/mvp/valid/simulation-game-logic-module-valid.json
|
||||
M harness/schemas/game-logic-module.schema.json
|
||||
M harness/schemas/validation-report.schema.json
|
||||
M harness/scripts/validate-harness.mjs
|
||||
?? harness/fixtures/mvp/invalid/game-logic-missing-build-graph-id-invalid.json
|
||||
?? harness/fixtures/mvp/invalid/game-logic-missing-export-signature-invalid.json
|
||||
?? harness/fixtures/mvp/invalid/game-logic-missing-runtime-sdk-contract-version-invalid.json
|
||||
?? harness/fixtures/mvp/invalid/game-logic-string-source-ref-invalid.json
|
||||
?? harness/fixtures/mvp/invalid/validation-report-disallowed-import-invalid.json
|
||||
?? harness/fixtures/mvp/invalid/validation-report-extra-properties-invalid.json
|
||||
?? harness/fixtures/mvp/invalid/validation-report-missing-created-at-invalid.json
|
||||
?? harness/fixtures/mvp/invalid/validation-report-missing-target-contract-invalid.json
|
||||
?? harness/fixtures/mvp/invalid/validation-report-missing-validator-version-invalid.json
|
||||
```
|
||||
|
||||
## Residual Notes
|
||||
|
||||
- `ValidationReport.result` 在 schema CLI 里对 failed report 会先触发 `SCHEMA_CONST`;但 full fixture gate 的 custom invalid path 仍覆盖 `VALIDATION_REPORT_FAILED`,reviewer 已确认不影响 Task1 fail-closed 目标。
|
||||
- `harness/scripts/validate-harness.mjs` 的新增逻辑仅服务 schema 无法表达的跨字段约束和 fixture routing,没有引入 S3 Task2+ business implementation。
|
||||
- 下一步必须进入 S3 Task2 shared contracts;不能跳过到 API compiler、logic compiler、runtime 或 conversion implementation。
|
||||
@ -17,6 +17,7 @@
|
||||
"window"
|
||||
],
|
||||
"ruleVersion": "mvp-static-logic-rules-v1",
|
||||
"validatorVersion": "mvp-s3-validator-v1",
|
||||
"result": "failed",
|
||||
"createdAt": "2026-06-01T10:00:00+08:00"
|
||||
}
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
"dynamic import"
|
||||
],
|
||||
"ruleVersion": "mvp-static-logic-rules-v1",
|
||||
"validatorVersion": "mvp-s3-validator-v1",
|
||||
"result": "failed",
|
||||
"createdAt": "2026-06-01T10:00:00+08:00"
|
||||
}
|
||||
|
||||
@ -0,0 +1,37 @@
|
||||
{
|
||||
"expectedReasonCode": "SCHEMA_REQUIRED",
|
||||
"id": "game-logic-module-missing-build-graph-id-001",
|
||||
"gameConfigId": "simulation-fixture-v1",
|
||||
"moduleType": "simulation_management",
|
||||
"sourceKind": "template_codegen",
|
||||
"sourceRef": {
|
||||
"gameConfigId": "simulation-fixture-v1",
|
||||
"templateVersion": "simulation-management@1",
|
||||
"buildInputPath": "apps/api/src/modules/simulation/fixtures/simulation-fixture-v1.game-config.json"
|
||||
},
|
||||
"artifactPath": "dist/game-logic/simulation-fixture-v1/logic.mjs",
|
||||
"checksum": "sha256:2222222222222222222222222222222222222222222222222222222222222222",
|
||||
"exportSignature": {
|
||||
"init": "(sdk, config) => GameState",
|
||||
"update": "(state, tick, sdk) => GameState",
|
||||
"render": "(state, sdk) => RenderCommand[]",
|
||||
"handleInput": "(state, input, sdk) => GameState",
|
||||
"onPause": "(state, sdk) => void",
|
||||
"onResume": "(state, sdk) => void"
|
||||
},
|
||||
"runtimeSdkContractVersion": "mvp-s3-runtime-sdk-v1",
|
||||
"validationReportId": "validation-report-simulation-fixture-v1",
|
||||
"languageSubset": "s3-safe-esm-game-logic-v1",
|
||||
"imports": [
|
||||
"@huijing/runtime-sdk"
|
||||
],
|
||||
"forbiddenApis": [],
|
||||
"entrypoints": [
|
||||
"init",
|
||||
"update",
|
||||
"render",
|
||||
"handleInput",
|
||||
"onPause",
|
||||
"onResume"
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
{
|
||||
"expectedReasonCode": "SCHEMA_REQUIRED",
|
||||
"id": "game-logic-module-missing-export-signature-001",
|
||||
"gameConfigId": "simulation-fixture-v1",
|
||||
"moduleType": "simulation_management",
|
||||
"sourceKind": "template_codegen",
|
||||
"sourceRef": {
|
||||
"gameConfigId": "simulation-fixture-v1",
|
||||
"templateVersion": "simulation-management@1",
|
||||
"buildInputPath": "apps/api/src/modules/simulation/fixtures/simulation-fixture-v1.game-config.json"
|
||||
},
|
||||
"artifactPath": "dist/game-logic/simulation-fixture-v1/logic.mjs",
|
||||
"checksum": "sha256:2222222222222222222222222222222222222222222222222222222222222222",
|
||||
"runtimeSdkContractVersion": "mvp-s3-runtime-sdk-v1",
|
||||
"validationReportId": "validation-report-simulation-fixture-v1",
|
||||
"buildGraphId": "build-graph-simulation-fixture-v1",
|
||||
"languageSubset": "s3-safe-esm-game-logic-v1",
|
||||
"imports": [
|
||||
"@huijing/runtime-sdk"
|
||||
],
|
||||
"forbiddenApis": [],
|
||||
"entrypoints": [
|
||||
"init",
|
||||
"update",
|
||||
"render",
|
||||
"handleInput",
|
||||
"onPause",
|
||||
"onResume"
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
{
|
||||
"expectedReasonCode": "SCHEMA_REQUIRED",
|
||||
"id": "game-logic-module-missing-runtime-sdk-contract-version-001",
|
||||
"gameConfigId": "simulation-fixture-v1",
|
||||
"moduleType": "simulation_management",
|
||||
"sourceKind": "template_codegen",
|
||||
"sourceRef": {
|
||||
"gameConfigId": "simulation-fixture-v1",
|
||||
"templateVersion": "simulation-management@1",
|
||||
"buildInputPath": "apps/api/src/modules/simulation/fixtures/simulation-fixture-v1.game-config.json"
|
||||
},
|
||||
"artifactPath": "dist/game-logic/simulation-fixture-v1/logic.mjs",
|
||||
"checksum": "sha256:2222222222222222222222222222222222222222222222222222222222222222",
|
||||
"exportSignature": {
|
||||
"init": "(sdk, config) => GameState",
|
||||
"update": "(state, tick, sdk) => GameState",
|
||||
"render": "(state, sdk) => RenderCommand[]",
|
||||
"handleInput": "(state, input, sdk) => GameState",
|
||||
"onPause": "(state, sdk) => void",
|
||||
"onResume": "(state, sdk) => void"
|
||||
},
|
||||
"validationReportId": "validation-report-simulation-fixture-v1",
|
||||
"buildGraphId": "build-graph-simulation-fixture-v1",
|
||||
"languageSubset": "s3-safe-esm-game-logic-v1",
|
||||
"imports": [
|
||||
"@huijing/runtime-sdk"
|
||||
],
|
||||
"forbiddenApis": [],
|
||||
"entrypoints": [
|
||||
"init",
|
||||
"update",
|
||||
"render",
|
||||
"handleInput",
|
||||
"onPause",
|
||||
"onResume"
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
{
|
||||
"expectedReasonCode": "SCHEMA_TYPE",
|
||||
"id": "game-logic-module-string-source-ref-001",
|
||||
"gameConfigId": "simulation-fixture-v1",
|
||||
"moduleType": "simulation_management",
|
||||
"sourceKind": "template_codegen",
|
||||
"sourceRef": "sha256:1111111111111111111111111111111111111111111111111111111111111111",
|
||||
"artifactPath": "dist/game-logic/simulation-fixture-v1/logic.mjs",
|
||||
"checksum": "sha256:2222222222222222222222222222222222222222222222222222222222222222",
|
||||
"exportSignature": {
|
||||
"init": "(sdk, config) => GameState",
|
||||
"update": "(state, tick, sdk) => GameState",
|
||||
"render": "(state, sdk) => RenderCommand[]",
|
||||
"handleInput": "(state, input, sdk) => GameState",
|
||||
"onPause": "(state, sdk) => void",
|
||||
"onResume": "(state, sdk) => void"
|
||||
},
|
||||
"runtimeSdkContractVersion": "mvp-s3-runtime-sdk-v1",
|
||||
"validationReportId": "validation-report-simulation-fixture-v1",
|
||||
"buildGraphId": "build-graph-simulation-fixture-v1",
|
||||
"languageSubset": "s3-safe-esm-game-logic-v1",
|
||||
"imports": [
|
||||
"@huijing/runtime-sdk"
|
||||
],
|
||||
"forbiddenApis": [],
|
||||
"entrypoints": [
|
||||
"init",
|
||||
"update",
|
||||
"render",
|
||||
"handleInput",
|
||||
"onPause",
|
||||
"onResume"
|
||||
]
|
||||
}
|
||||
@ -1,20 +1,39 @@
|
||||
{
|
||||
"expectedReasonCode": "VALIDATION_REPORT_REQUIRED",
|
||||
"id": "game-logic-module-no-report-001",
|
||||
"gameConfigId": "game-config-simulation-001",
|
||||
"moduleType": "simulation_core_loop",
|
||||
"sourceKind": "generated_static_module",
|
||||
"sourceRef": "sha256:source-simulation-core-v1",
|
||||
"artifactPath": "artifacts/game-logic/simulation-core.mjs",
|
||||
"checksum": "sha256:logic-module-simulation-core-v1",
|
||||
"expectedReasonCode": "GAME_LOGIC_VALIDATION_STATUS_SELF_REPORTED",
|
||||
"id": "game-logic-module-self-reported-validation-001",
|
||||
"gameConfigId": "simulation-fixture-v1",
|
||||
"moduleType": "simulation_management",
|
||||
"sourceKind": "template_codegen",
|
||||
"sourceRef": {
|
||||
"gameConfigId": "simulation-fixture-v1",
|
||||
"templateVersion": "simulation-management@1",
|
||||
"buildInputPath": "apps/api/src/modules/simulation/fixtures/simulation-fixture-v1.game-config.json"
|
||||
},
|
||||
"artifactPath": "dist/game-logic/simulation-fixture-v1/logic.mjs",
|
||||
"checksum": "sha256:2222222222222222222222222222222222222222222222222222222222222222",
|
||||
"exportSignature": {
|
||||
"init": "(sdk, config) => GameState",
|
||||
"update": "(state, tick, sdk) => GameState",
|
||||
"render": "(state, sdk) => RenderCommand[]",
|
||||
"handleInput": "(state, input, sdk) => GameState",
|
||||
"onPause": "(state, sdk) => void",
|
||||
"onResume": "(state, sdk) => void"
|
||||
},
|
||||
"runtimeSdkContractVersion": "mvp-s3-runtime-sdk-v1",
|
||||
"validationReportId": "missing-validation-report-999",
|
||||
"languageSubset": "mvp-safe-js-expression-subset",
|
||||
"imports": [],
|
||||
"buildGraphId": "build-graph-simulation-fixture-v1",
|
||||
"languageSubset": "s3-safe-esm-game-logic-v1",
|
||||
"imports": [
|
||||
"@huijing/runtime-sdk"
|
||||
],
|
||||
"forbiddenApis": [],
|
||||
"entrypoints": [
|
||||
"createInitialState",
|
||||
"applyTurnAction",
|
||||
"deriveOutcome"
|
||||
"init",
|
||||
"update",
|
||||
"render",
|
||||
"handleInput",
|
||||
"onPause",
|
||||
"onResume"
|
||||
],
|
||||
"validationStatus": "validated"
|
||||
}
|
||||
|
||||
@ -2,20 +2,27 @@
|
||||
"expectedReasonCode": "VALIDATION_REPORT_FAILED",
|
||||
"id": "validation-report-failed-001",
|
||||
"targetContract": "GameLogicModule",
|
||||
"artifactPath": "artifacts/game-logic/simulation-core.mjs",
|
||||
"sourceHash": "sha256:source-simulation-core-v1",
|
||||
"checksum": "sha256:logic-module-simulation-core-v1",
|
||||
"artifactPath": "dist/game-logic/simulation-fixture-v1/logic.mjs",
|
||||
"sourceHash": "sha256:1111111111111111111111111111111111111111111111111111111111111111",
|
||||
"checksum": "sha256:2222222222222222222222222222222222222222222222222222222222222222",
|
||||
"checkedFiles": [
|
||||
"artifacts/game-logic/simulation-core.mjs"
|
||||
"dist/game-logic/simulation-fixture-v1/logic.mjs"
|
||||
],
|
||||
"importGraph": {
|
||||
"artifacts/game-logic/simulation-core.mjs": []
|
||||
"logic.mjs": [
|
||||
"@huijing/runtime-sdk"
|
||||
]
|
||||
},
|
||||
"allowedImports": [],
|
||||
"allowedImports": [
|
||||
"@huijing/runtime-sdk",
|
||||
"@huijing/rules",
|
||||
"@huijing/math"
|
||||
],
|
||||
"deniedSymbols": [
|
||||
"unsafe_global_access"
|
||||
],
|
||||
"ruleVersion": "mvp-static-logic-rules-v1",
|
||||
"ruleVersion": "game-logic-allowlist-v1",
|
||||
"validatorVersion": "mvp-s3-validator-v1",
|
||||
"result": "failed",
|
||||
"createdAt": "2026-06-01T10:00:00+08:00"
|
||||
"createdAt": "2026-06-04T00:00:00.000Z"
|
||||
}
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
"WebAudio"
|
||||
],
|
||||
"ruleVersion": "mvp-static-logic-rules-v1",
|
||||
"validatorVersion": "mvp-s3-validator-v1",
|
||||
"result": "passed",
|
||||
"createdAt": "2026-06-01T10:00:00+08:00"
|
||||
}
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
"node:fs"
|
||||
],
|
||||
"ruleVersion": "mvp-static-logic-rules-v1",
|
||||
"validatorVersion": "mvp-s3-validator-v1",
|
||||
"result": "failed",
|
||||
"createdAt": "2026-06-01T10:00:00+08:00"
|
||||
}
|
||||
|
||||
@ -2,19 +2,26 @@
|
||||
"expectedReasonCode": "VALIDATION_REPORT_CHECKSUM_MISMATCH",
|
||||
"id": "validation-report-checksum-mismatch-001",
|
||||
"targetContract": "GameLogicModule",
|
||||
"artifactPath": "artifacts/game-logic/simulation-core.mjs",
|
||||
"sourceHash": "sha256:source-simulation-core-v1",
|
||||
"checksum": "sha256:wrong-logic-module-checksum",
|
||||
"expectedModuleChecksum": "sha256:logic-module-simulation-core-v1",
|
||||
"artifactPath": "dist/game-logic/simulation-fixture-v1/logic.mjs",
|
||||
"sourceHash": "sha256:1111111111111111111111111111111111111111111111111111111111111111",
|
||||
"checksum": "sha256:9999999999999999999999999999999999999999999999999999999999999999",
|
||||
"expectedModuleChecksum": "sha256:2222222222222222222222222222222222222222222222222222222222222222",
|
||||
"checkedFiles": [
|
||||
"artifacts/game-logic/simulation-core.mjs"
|
||||
"dist/game-logic/simulation-fixture-v1/logic.mjs"
|
||||
],
|
||||
"importGraph": {
|
||||
"artifacts/game-logic/simulation-core.mjs": []
|
||||
"logic.mjs": [
|
||||
"@huijing/runtime-sdk"
|
||||
]
|
||||
},
|
||||
"allowedImports": [],
|
||||
"allowedImports": [
|
||||
"@huijing/runtime-sdk",
|
||||
"@huijing/rules",
|
||||
"@huijing/math"
|
||||
],
|
||||
"deniedSymbols": [],
|
||||
"ruleVersion": "mvp-static-logic-rules-v1",
|
||||
"ruleVersion": "game-logic-allowlist-v1",
|
||||
"validatorVersion": "mvp-s3-validator-v1",
|
||||
"result": "passed",
|
||||
"createdAt": "2026-06-01T10:00:00+08:00"
|
||||
"createdAt": "2026-06-04T00:00:00.000Z"
|
||||
}
|
||||
|
||||
@ -0,0 +1,27 @@
|
||||
{
|
||||
"expectedReasonCode": "VALIDATION_REPORT_IMPORT_NOT_ALLOWED",
|
||||
"id": "validation-report-disallowed-import-001",
|
||||
"targetContract": "GameLogicModule",
|
||||
"artifactPath": "dist/game-logic/simulation-fixture-v1/logic.mjs",
|
||||
"sourceHash": "sha256:1111111111111111111111111111111111111111111111111111111111111111",
|
||||
"checksum": "sha256:2222222222222222222222222222222222222222222222222222222222222222",
|
||||
"checkedFiles": [
|
||||
"dist/game-logic/simulation-fixture-v1/logic.mjs"
|
||||
],
|
||||
"importGraph": {
|
||||
"logic.mjs": [
|
||||
"@huijing/runtime-sdk",
|
||||
"@huijing/unsafe-renderer"
|
||||
]
|
||||
},
|
||||
"allowedImports": [
|
||||
"@huijing/runtime-sdk",
|
||||
"@huijing/rules",
|
||||
"@huijing/math"
|
||||
],
|
||||
"deniedSymbols": [],
|
||||
"ruleVersion": "game-logic-allowlist-v1",
|
||||
"validatorVersion": "mvp-s3-validator-v1",
|
||||
"result": "passed",
|
||||
"createdAt": "2026-06-04T00:00:00.000Z"
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
{
|
||||
"expectedReasonCode": "SCHEMA_ADDITIONAL_PROPERTY",
|
||||
"id": "validation-report-extra-properties-001",
|
||||
"targetContract": "GameLogicModule",
|
||||
"artifactPath": "dist/game-logic/simulation-fixture-v1/logic.mjs",
|
||||
"sourceHash": "sha256:1111111111111111111111111111111111111111111111111111111111111111",
|
||||
"checksum": "sha256:2222222222222222222222222222222222222222222222222222222222222222",
|
||||
"checkedFiles": [
|
||||
"dist/game-logic/simulation-fixture-v1/logic.mjs"
|
||||
],
|
||||
"importGraph": {
|
||||
"logic.mjs": [
|
||||
"@huijing/runtime-sdk"
|
||||
]
|
||||
},
|
||||
"allowedImports": [
|
||||
"@huijing/runtime-sdk",
|
||||
"@huijing/rules",
|
||||
"@huijing/math"
|
||||
],
|
||||
"deniedSymbols": [],
|
||||
"ruleVersion": "game-logic-allowlist-v1",
|
||||
"validatorVersion": "mvp-s3-validator-v1",
|
||||
"result": "passed",
|
||||
"createdAt": "2026-06-04T00:00:00.000Z",
|
||||
"selfReportedGate": "passed"
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
{
|
||||
"expectedReasonCode": "VALIDATION_REPORT_REQUIRED_FIELD_MISSING",
|
||||
"id": "validation-report-missing-created-at-001",
|
||||
"targetContract": "GameLogicModule",
|
||||
"artifactPath": "dist/game-logic/simulation-fixture-v1/logic.mjs",
|
||||
"sourceHash": "sha256:1111111111111111111111111111111111111111111111111111111111111111",
|
||||
"checksum": "sha256:2222222222222222222222222222222222222222222222222222222222222222",
|
||||
"checkedFiles": [
|
||||
"dist/game-logic/simulation-fixture-v1/logic.mjs"
|
||||
],
|
||||
"importGraph": {
|
||||
"logic.mjs": [
|
||||
"@huijing/runtime-sdk"
|
||||
]
|
||||
},
|
||||
"allowedImports": [
|
||||
"@huijing/runtime-sdk",
|
||||
"@huijing/rules",
|
||||
"@huijing/math"
|
||||
],
|
||||
"deniedSymbols": [],
|
||||
"ruleVersion": "game-logic-allowlist-v1",
|
||||
"validatorVersion": "mvp-s3-validator-v1",
|
||||
"result": "passed"
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
{
|
||||
"expectedReasonCode": "VALIDATION_REPORT_REQUIRED_FIELD_MISSING",
|
||||
"id": "validation-report-missing-target-contract-001",
|
||||
"artifactPath": "dist/game-logic/simulation-fixture-v1/logic.mjs",
|
||||
"sourceHash": "sha256:1111111111111111111111111111111111111111111111111111111111111111",
|
||||
"checksum": "sha256:2222222222222222222222222222222222222222222222222222222222222222",
|
||||
"checkedFiles": [
|
||||
"dist/game-logic/simulation-fixture-v1/logic.mjs"
|
||||
],
|
||||
"importGraph": {
|
||||
"logic.mjs": [
|
||||
"@huijing/runtime-sdk"
|
||||
]
|
||||
},
|
||||
"allowedImports": [
|
||||
"@huijing/runtime-sdk",
|
||||
"@huijing/rules",
|
||||
"@huijing/math"
|
||||
],
|
||||
"deniedSymbols": [],
|
||||
"ruleVersion": "game-logic-allowlist-v1",
|
||||
"validatorVersion": "mvp-s3-validator-v1",
|
||||
"result": "passed",
|
||||
"createdAt": "2026-06-04T00:00:00.000Z"
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
{
|
||||
"expectedReasonCode": "VALIDATION_REPORT_REQUIRED_FIELD_MISSING",
|
||||
"id": "validation-report-missing-validator-version-001",
|
||||
"targetContract": "GameLogicModule",
|
||||
"artifactPath": "dist/game-logic/simulation-fixture-v1/logic.mjs",
|
||||
"sourceHash": "sha256:1111111111111111111111111111111111111111111111111111111111111111",
|
||||
"checksum": "sha256:2222222222222222222222222222222222222222222222222222222222222222",
|
||||
"checkedFiles": [
|
||||
"dist/game-logic/simulation-fixture-v1/logic.mjs"
|
||||
],
|
||||
"importGraph": {
|
||||
"logic.mjs": [
|
||||
"@huijing/runtime-sdk"
|
||||
]
|
||||
},
|
||||
"allowedImports": [
|
||||
"@huijing/runtime-sdk",
|
||||
"@huijing/rules",
|
||||
"@huijing/math"
|
||||
],
|
||||
"deniedSymbols": [],
|
||||
"ruleVersion": "game-logic-allowlist-v1",
|
||||
"result": "passed",
|
||||
"createdAt": "2026-06-04T00:00:00.000Z"
|
||||
}
|
||||
@ -1,18 +1,25 @@
|
||||
{
|
||||
"id": "validation-report-logic-simulation-core-001",
|
||||
"id": "validation-report-simulation-fixture-v1",
|
||||
"targetContract": "GameLogicModule",
|
||||
"artifactPath": "artifacts/game-logic/simulation-core.mjs",
|
||||
"sourceHash": "sha256:source-simulation-core-v1",
|
||||
"checksum": "sha256:logic-module-simulation-core-v1",
|
||||
"artifactPath": "dist/game-logic/simulation-fixture-v1/logic.mjs",
|
||||
"sourceHash": "sha256:1111111111111111111111111111111111111111111111111111111111111111",
|
||||
"checksum": "sha256:2222222222222222222222222222222222222222222222222222222222222222",
|
||||
"checkedFiles": [
|
||||
"artifacts/game-logic/simulation-core.mjs"
|
||||
"dist/game-logic/simulation-fixture-v1/logic.mjs"
|
||||
],
|
||||
"importGraph": {
|
||||
"artifacts/game-logic/simulation-core.mjs": []
|
||||
"logic.mjs": [
|
||||
"@huijing/runtime-sdk"
|
||||
]
|
||||
},
|
||||
"allowedImports": [],
|
||||
"allowedImports": [
|
||||
"@huijing/runtime-sdk",
|
||||
"@huijing/rules",
|
||||
"@huijing/math"
|
||||
],
|
||||
"deniedSymbols": [],
|
||||
"ruleVersion": "mvp-static-logic-rules-v1",
|
||||
"ruleVersion": "game-logic-allowlist-v1",
|
||||
"validatorVersion": "mvp-s3-validator-v1",
|
||||
"result": "passed",
|
||||
"createdAt": "2026-06-01T10:00:00+08:00"
|
||||
"createdAt": "2026-06-04T00:00:00.000Z"
|
||||
}
|
||||
|
||||
@ -1,19 +1,37 @@
|
||||
{
|
||||
"id": "game-logic-module-simulation-core-001",
|
||||
"gameConfigId": "game-config-simulation-001",
|
||||
"moduleType": "simulation_core_loop",
|
||||
"sourceKind": "generated_static_module",
|
||||
"sourceRef": "sha256:source-simulation-core-v1",
|
||||
"artifactPath": "artifacts/game-logic/simulation-core.mjs",
|
||||
"checksum": "sha256:logic-module-simulation-core-v1",
|
||||
"validationReportId": "validation-report-logic-simulation-core-001",
|
||||
"languageSubset": "mvp-safe-js-expression-subset",
|
||||
"imports": [],
|
||||
"id": "game-logic-module-simulation-fixture-v1",
|
||||
"gameConfigId": "simulation-fixture-v1",
|
||||
"moduleType": "simulation_management",
|
||||
"sourceKind": "template_codegen",
|
||||
"sourceRef": {
|
||||
"gameConfigId": "simulation-fixture-v1",
|
||||
"templateVersion": "simulation-management@1",
|
||||
"buildInputPath": "apps/api/src/modules/simulation/fixtures/simulation-fixture-v1.game-config.json"
|
||||
},
|
||||
"artifactPath": "dist/game-logic/simulation-fixture-v1/logic.mjs",
|
||||
"checksum": "sha256:2222222222222222222222222222222222222222222222222222222222222222",
|
||||
"exportSignature": {
|
||||
"init": "(sdk, config) => GameState",
|
||||
"update": "(state, tick, sdk) => GameState",
|
||||
"render": "(state, sdk) => RenderCommand[]",
|
||||
"handleInput": "(state, input, sdk) => GameState",
|
||||
"onPause": "(state, sdk) => void",
|
||||
"onResume": "(state, sdk) => void"
|
||||
},
|
||||
"runtimeSdkContractVersion": "mvp-s3-runtime-sdk-v1",
|
||||
"validationReportId": "validation-report-simulation-fixture-v1",
|
||||
"buildGraphId": "build-graph-simulation-fixture-v1",
|
||||
"languageSubset": "s3-safe-esm-game-logic-v1",
|
||||
"imports": [
|
||||
"@huijing/runtime-sdk"
|
||||
],
|
||||
"forbiddenApis": [],
|
||||
"entrypoints": [
|
||||
"createInitialState",
|
||||
"applyTurnAction",
|
||||
"deriveOutcome"
|
||||
],
|
||||
"validationStatus": "validated"
|
||||
"init",
|
||||
"update",
|
||||
"render",
|
||||
"handleInput",
|
||||
"onPause",
|
||||
"onResume"
|
||||
]
|
||||
}
|
||||
|
||||
@ -2,21 +2,45 @@
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "GameLogicModule",
|
||||
"type": "object",
|
||||
"required": ["id", "gameConfigId", "moduleType", "sourceKind", "sourceRef", "artifactPath", "checksum", "validationReportId", "languageSubset", "imports", "forbiddenApis", "entrypoints", "validationStatus"],
|
||||
"required": ["id", "gameConfigId", "moduleType", "sourceKind", "sourceRef", "artifactPath", "checksum", "exportSignature", "runtimeSdkContractVersion", "validationReportId", "buildGraphId", "languageSubset", "imports", "forbiddenApis", "entrypoints"],
|
||||
"forbiddenProperties": ["validationStatus"],
|
||||
"properties": {
|
||||
"id": { "type": "string" },
|
||||
"gameConfigId": { "type": "string" },
|
||||
"moduleType": { "type": "string" },
|
||||
"sourceKind": { "type": "string" },
|
||||
"sourceRef": { "type": "string" },
|
||||
"sourceRef": {
|
||||
"type": "object",
|
||||
"required": ["gameConfigId", "templateVersion", "buildInputPath"],
|
||||
"properties": {
|
||||
"gameConfigId": { "type": "string" },
|
||||
"templateVersion": { "type": "string" },
|
||||
"buildInputPath": { "type": "string" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"artifactPath": { "type": "string" },
|
||||
"checksum": { "type": "string" },
|
||||
"exportSignature": {
|
||||
"type": "object",
|
||||
"required": ["init", "update", "render", "handleInput", "onPause", "onResume"],
|
||||
"properties": {
|
||||
"init": { "type": "string" },
|
||||
"update": { "type": "string" },
|
||||
"render": { "type": "string" },
|
||||
"handleInput": { "type": "string" },
|
||||
"onPause": { "type": "string" },
|
||||
"onResume": { "type": "string" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"runtimeSdkContractVersion": { "type": "string" },
|
||||
"validationReportId": { "type": "string" },
|
||||
"buildGraphId": { "type": "string" },
|
||||
"languageSubset": { "type": "string" },
|
||||
"imports": { "type": "array" },
|
||||
"forbiddenApis": { "type": "array" },
|
||||
"entrypoints": { "type": "array" },
|
||||
"validationStatus": { "type": "string" }
|
||||
"entrypoints": { "type": "array" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "ValidationReport",
|
||||
"type": "object",
|
||||
"required": ["id", "targetContract", "artifactPath", "sourceHash", "checksum", "checkedFiles", "importGraph", "allowedImports", "deniedSymbols", "ruleVersion", "result", "createdAt"],
|
||||
"required": ["id", "targetContract", "artifactPath", "sourceHash", "checksum", "checkedFiles", "importGraph", "allowedImports", "deniedSymbols", "ruleVersion", "validatorVersion", "result", "createdAt"],
|
||||
"properties": {
|
||||
"id": { "type": "string" },
|
||||
"targetContract": { "type": "string" },
|
||||
@ -14,7 +14,8 @@
|
||||
"allowedImports": { "type": "array" },
|
||||
"deniedSymbols": { "type": "array" },
|
||||
"ruleVersion": { "type": "string" },
|
||||
"result": { "enum": ["passed", "failed"] },
|
||||
"validatorVersion": { "type": "string" },
|
||||
"result": { "const": "passed" },
|
||||
"createdAt": { "type": "string" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
|
||||
@ -31,7 +31,16 @@ const mvpSummaryFixtureNames = [
|
||||
"game-logic-validation-report-web-only-sdk-invalid.json",
|
||||
"validation-report-checksum-mismatch-invalid.json",
|
||||
"game-logic-validated-without-report-invalid.json",
|
||||
"game-logic-validation-report-failed-invalid.json"
|
||||
"game-logic-missing-export-signature-invalid.json",
|
||||
"game-logic-missing-runtime-sdk-contract-version-invalid.json",
|
||||
"game-logic-missing-build-graph-id-invalid.json",
|
||||
"game-logic-string-source-ref-invalid.json",
|
||||
"game-logic-validation-report-failed-invalid.json",
|
||||
"validation-report-disallowed-import-invalid.json",
|
||||
"validation-report-missing-target-contract-invalid.json",
|
||||
"validation-report-missing-validator-version-invalid.json",
|
||||
"validation-report-missing-created-at-invalid.json",
|
||||
"validation-report-extra-properties-invalid.json"
|
||||
];
|
||||
|
||||
const allowedAgentTaskPairs = new Map([
|
||||
@ -296,13 +305,37 @@ function schemaTargetsForFixture(file) {
|
||||
if (fileName === "design-section-patch-valid.json" || fileName === "design-section-patch-checksum-conflict-invalid.json") return ["DesignSectionPatch"];
|
||||
if (fileName === "asset-manifest-valid.json" || fileName === "resource-over-budget-invalid.json") return ["AssetManifest"];
|
||||
if (fileName === "simulation-game-ir-valid.json") return ["GameIR"];
|
||||
if (fileName === "simulation-game-logic-module-valid.json" || fileName === "game-logic-validated-without-report-invalid.json") return ["GameLogicModule"];
|
||||
if (
|
||||
fileName === "simulation-game-logic-module-valid.json" ||
|
||||
[
|
||||
"game-logic-validated-without-report-invalid.json",
|
||||
"game-logic-missing-export-signature-invalid.json",
|
||||
"game-logic-missing-runtime-sdk-contract-version-invalid.json",
|
||||
"game-logic-missing-build-graph-id-invalid.json",
|
||||
"game-logic-string-source-ref-invalid.json"
|
||||
].includes(fileName)
|
||||
) return ["GameLogicModule"];
|
||||
if (fileName === "simulation-game-package-four-profiles-valid.json") return ["GamePackage"];
|
||||
if (fileName === "minigame-code-conversion-valid.json" || ["missing-adapter-invalid.json", "web-package-disguised-as-minigame-invalid.json"].includes(fileName)) return ["MiniGameCodeConversion"];
|
||||
if (fileName.startsWith("minigame-project-") || ["checksum-mismatch-invalid.json", "empty-minigame-project-shell-invalid.json", "missing-entry-config-invalid.json"].includes(fileName)) return ["MiniGameProject"];
|
||||
if (fileName.endsWith("conversion-report-valid.json") || ["forged-conversion-result-invalid.json", "external-script-invalid.json", "web-only-sdk-invalid.json"].includes(fileName)) return ["ConversionReport"];
|
||||
if (fileName === "runtime-smoke-evidence-valid.json" || fileName === "runtime-smoke-failed-as-passed-invalid.json") return ["RuntimeSmokeEvidence"];
|
||||
if (fileName === "game-logic-validation-report-valid.json" || ["dom-bom-usage-invalid.json", "dynamic-execution-invalid.json", "node-api-usage-invalid.json", "game-logic-validation-report-web-only-sdk-invalid.json", "validation-report-checksum-mismatch-invalid.json", "game-logic-validation-report-failed-invalid.json"].includes(fileName)) return ["ValidationReport"];
|
||||
if (
|
||||
fileName === "game-logic-validation-report-valid.json" ||
|
||||
[
|
||||
"dom-bom-usage-invalid.json",
|
||||
"dynamic-execution-invalid.json",
|
||||
"node-api-usage-invalid.json",
|
||||
"game-logic-validation-report-web-only-sdk-invalid.json",
|
||||
"validation-report-checksum-mismatch-invalid.json",
|
||||
"game-logic-validation-report-failed-invalid.json",
|
||||
"validation-report-disallowed-import-invalid.json",
|
||||
"validation-report-missing-target-contract-invalid.json",
|
||||
"validation-report-missing-validator-version-invalid.json",
|
||||
"validation-report-missing-created-at-invalid.json",
|
||||
"validation-report-extra-properties-invalid.json"
|
||||
].includes(fileName)
|
||||
) return ["ValidationReport"];
|
||||
if (fileName === "devtool-import-evidence-valid.json" || ["blocker-as-passed-invalid.json", "failed-import-used-as-passed-invalid.json"].includes(fileName)) return ["DevToolImportEvidence"];
|
||||
if (fileName === "devtool-import-evidence-record-valid.json" || ["note-as-evidence-invalid.json", "evidence-record-non-passed-import-invalid.json"].includes(fileName)) return ["EvidenceRecord"];
|
||||
if (fileName === "channel-readiness-note-valid.json" || fileName === "channel-readiness-as-review-invalid.json") return ["ChannelReadinessNote"];
|
||||
@ -406,6 +439,11 @@ function importGraphSpecifiers(data) {
|
||||
return Object.values(data.importGraph ?? {}).flatMap((item) => asArray(item));
|
||||
}
|
||||
|
||||
function validationReportHasDisallowedImport(data) {
|
||||
const allowedImports = new Set(asArray(data.allowedImports));
|
||||
return importGraphSpecifiers(data).some((specifier) => !allowedImports.has(specifier));
|
||||
}
|
||||
|
||||
function hasAny(values, candidates) {
|
||||
const valueSet = new Set(values);
|
||||
return candidates.some((candidate) => valueSet.has(candidate));
|
||||
@ -448,7 +486,21 @@ function conversionReportMissingRequired(data) {
|
||||
}
|
||||
|
||||
function validationReportMissingRequired(data) {
|
||||
return ["sourceHash", "checksum", "importGraph", "ruleVersion", "result"].some((field) => !valueExists(data[field]));
|
||||
return [
|
||||
"id",
|
||||
"targetContract",
|
||||
"artifactPath",
|
||||
"sourceHash",
|
||||
"checksum",
|
||||
"checkedFiles",
|
||||
"importGraph",
|
||||
"allowedImports",
|
||||
"deniedSymbols",
|
||||
"ruleVersion",
|
||||
"validatorVersion",
|
||||
"result",
|
||||
"createdAt"
|
||||
].some((field) => !valueExists(data[field]));
|
||||
}
|
||||
|
||||
function customContractSetRejection(contracts, rawData, context, fileName = "") {
|
||||
@ -481,10 +533,8 @@ function customContractSetRejection(contracts, rawData, context, fileName = "")
|
||||
|
||||
if (contracts.has("DesignSectionPatch") && data.conflictStatus === "conflict") return rejectedWhen(true, "CHECKSUM_CONFLICT");
|
||||
|
||||
if (contracts.has("GameLogicModule") && data.validationStatus === "validated") {
|
||||
const report = context.mvp.validationReportsById.get(data.validationReportId);
|
||||
if (!report || report.result !== "passed") return rejectedWhen(true, "VALIDATION_REPORT_REQUIRED", data.validationReportId);
|
||||
if (report.checksum !== data.checksum || report.sourceHash !== data.sourceRef) return rejectedWhen(true, "VALIDATION_REPORT_CHECKSUM_MISMATCH", data.validationReportId);
|
||||
if (contracts.has("GameLogicModule") && Object.prototype.hasOwnProperty.call(data, "validationStatus")) {
|
||||
return rejectedWhen(true, "GAME_LOGIC_VALIDATION_STATUS_SELF_REPORTED", data.validationStatus);
|
||||
}
|
||||
|
||||
if (contracts.has("GamePackage") && gamePackageMissingProfile(data)) return rejectedWhen(true, "GAME_PACKAGE_PROFILE_MISSING");
|
||||
@ -531,6 +581,8 @@ function customContractSetRejection(contracts, rawData, context, fileName = "")
|
||||
return rejectedWhen(true, "DYNAMIC_EXECUTION_FORBIDDEN");
|
||||
}
|
||||
if (validationReportMissingRequired(data)) return rejectedWhen(true, "VALIDATION_REPORT_REQUIRED_FIELD_MISSING");
|
||||
// importGraph 和 allowedImports 是跨字段约束,当前轻量 JSON Schema 校验器无法声明式表达。
|
||||
if (validationReportHasDisallowedImport(data)) return rejectedWhen(true, "VALIDATION_REPORT_IMPORT_NOT_ALLOWED");
|
||||
if (data.expectedModuleChecksum && data.checksum !== data.expectedModuleChecksum) return rejectedWhen(true, "VALIDATION_REPORT_CHECKSUM_MISMATCH");
|
||||
if (data.result !== "passed") return rejectedWhen(true, "VALIDATION_REPORT_FAILED");
|
||||
}
|
||||
@ -572,8 +624,12 @@ function customContractRejection(contract, rawData, context) {
|
||||
}
|
||||
|
||||
function customContractRejectionBeforeSchema(contract, rawData) {
|
||||
if (contract !== "ValidationReport") return rejectedWhen(false, null);
|
||||
const data = stripFixtureMetadata(rawData);
|
||||
// descriptor 自报 gate 状态需要在 schema 前拒绝,避免 CLI 返回泛化的额外字段错误。
|
||||
if (contract === "GameLogicModule" && Object.prototype.hasOwnProperty.call(data ?? {}, "validationStatus")) {
|
||||
return rejectedWhen(true, "GAME_LOGIC_VALIDATION_STATUS_SELF_REPORTED", data.validationStatus);
|
||||
}
|
||||
if (contract !== "ValidationReport") return rejectedWhen(false, null);
|
||||
// diagnostics 不在当前 schema 中;只把这个已知语义错误前置,避免其它 malformed payload 绕过 schema。
|
||||
if (data?.targetContract === "GameLogicModule" && diagnosticsHaveCode(data, "WEB_ONLY_SDK_USAGE")) {
|
||||
return rejectedWhen(true, "WEB_ONLY_SDK_USAGE");
|
||||
@ -937,11 +993,11 @@ function validateFixtures(mappings, context) {
|
||||
check(exists("harness/fixtures/manifests/invalid-csp-violation.json"), "CSP violation fixture missing");
|
||||
for (const name of mvpSummaryFixtureNames) check(loadedFixtureNames.includes(name), `summary fixture ${name} was not loaded`);
|
||||
check(validFixtures === 45, `expected 45 valid fixtures, got ${validFixtures}`);
|
||||
check(invalidFixtures === 51, `expected 51 invalid fixtures, got ${invalidFixtures}`);
|
||||
check(invalidFixtures === 60, `expected 60 invalid fixtures, got ${invalidFixtures}`);
|
||||
check(foundationValidFixtures === 21, `expected 21 foundation valid fixtures, got ${foundationValidFixtures}`);
|
||||
check(mvpValidFixtures === 24, `expected 24 MVP valid fixtures, got ${mvpValidFixtures}`);
|
||||
check(foundationInvalidFixtures === 21, `expected 21 foundation invalid fixtures, got ${foundationInvalidFixtures}`);
|
||||
check(mvpInvalidFixtures === 30, `expected 30 MVP invalid fixtures, got ${mvpInvalidFixtures}`);
|
||||
check(mvpInvalidFixtures === 39, `expected 39 MVP invalid fixtures, got ${mvpInvalidFixtures}`);
|
||||
|
||||
return { validFixtures, invalidFixtures, foundationValidFixtures, mvpValidFixtures, foundationInvalidFixtures, mvpInvalidFixtures, invalidFixturesRejected, invalidReasonCounts, loadedFixtureNames };
|
||||
}
|
||||
@ -967,7 +1023,7 @@ function main() {
|
||||
const { validFixtures, invalidFixtures, foundationValidFixtures, mvpValidFixtures, foundationInvalidFixtures, mvpInvalidFixtures, invalidFixturesRejected, invalidReasonCounts, loadedFixtureNames } = validateFixtures(tools.toolTransitionMapping, context);
|
||||
|
||||
if (checkInvalidOnly) {
|
||||
check(invalidFixtures === 51, `expected 51 invalid fixtures, got ${invalidFixtures}`);
|
||||
check(invalidFixtures === 60, `expected 60 invalid fixtures, got ${invalidFixtures}`);
|
||||
check(invalidFixturesRejected === invalidFixtures, `expected every invalid fixture rejected, got ${invalidFixturesRejected}/${invalidFixtures}`);
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user