phasegate 0.229.0 → 0.264.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/CHANGELOG.md +73 -1
  2. package/README.ja.md +31 -1
  3. package/README.md +30 -0
  4. package/docs/ADR/031-world-model-ownership-and-corpus-lifecycle.md +155 -0
  5. package/docs/ADR/032-world-node-identity.md +198 -0
  6. package/docs/ADR/033-world-snapshot-canonicalization.md +246 -0
  7. package/docs/ADR/034-world-constraint-semantics.md +218 -0
  8. package/docs/ADR/035-world-adoption-baseline-and-waiver.md +341 -0
  9. package/docs/ADR/036-world-model-and-doc-freshness.md +169 -0
  10. package/docs/ADR/037-world-cli-and-output-contract.md +398 -0
  11. package/docs/contracts/attestation-v2.schema.json +110 -0
  12. package/docs/contracts/requirement-test-matrix.schema.json +15 -0
  13. package/docs/contracts/world-baseline.schema.json +35 -0
  14. package/docs/contracts/world-constraints.schema.json +56 -0
  15. package/docs/contracts/world-debts.schema.json +32 -0
  16. package/docs/contracts/world-obligation-report.schema.json +259 -0
  17. package/docs/contracts/world-waivers.schema.json +32 -0
  18. package/docs/guide/cli-reference.md +45 -0
  19. package/docs/guide/configuration.md +56 -0
  20. package/docs/templates/ci/aidlc-gate.yml +42 -2
  21. package/package.json +1 -1
  22. package/scripts/harness/agent-integration/application/dto/open-world-obligations-context-dto.ts +30 -0
  23. package/scripts/harness/agent-integration/application/ports/world-obligations-query-port.ts +30 -0
  24. package/scripts/harness/agent-integration/application/usecases/get-open-world-obligations-context-usecase.ts +66 -0
  25. package/scripts/harness/agent-integration/infrastructure/adapters/world-model-open-obligations-query-adapter.ts +58 -0
  26. package/scripts/harness/agent-integration/presentation/session-start-hook.ts +37 -1
  27. package/scripts/harness/agent-integration/presentation/world-obligations-session-context.ts +60 -0
  28. package/scripts/harness/attestation/application/dto/attestation-document.ts +17 -4
  29. package/scripts/harness/attestation/application/mappers/attestation-record-mapper.ts +55 -4
  30. package/scripts/harness/attestation/application/ports/sha256-capability.ts +25 -0
  31. package/scripts/harness/attestation/application/ports/world-snapshot-root-provider.ts +10 -0
  32. package/scripts/harness/attestation/application/usecases/produce-attestation-usecase.ts +33 -7
  33. package/scripts/harness/attestation/composition-root.ts +19 -4
  34. package/scripts/harness/attestation/domain/entities/attestation-record.ts +37 -2
  35. package/scripts/harness/attestation/index.ts +13 -2
  36. package/scripts/harness/attestation/infrastructure/adapters/node-crypto-content-hasher-adapter.ts +6 -6
  37. package/scripts/harness/attestation/infrastructure/adapters/node-crypto-sha256-capability.ts +19 -0
  38. package/scripts/harness/attestation/presentation/handlers/attest-handler.ts +5 -2
  39. package/scripts/harness/config-foundation/application/mappers/validator-system-config-mapper.ts +35 -8
  40. package/scripts/harness/config-foundation/application/mappers/world-model-config-mapper.ts +15 -0
  41. package/scripts/harness/config-foundation/domain/harness-config.ts +67 -87
  42. package/scripts/harness/config-foundation/domain/services/preset-resolution-service.ts +77 -88
  43. package/scripts/harness/config-foundation/domain/value-objects/world-config.ts +198 -0
  44. package/scripts/harness/config-foundation/index.ts +14 -15
  45. package/scripts/harness/config-foundation/infrastructure/presets/minimal.json +24 -0
  46. package/scripts/harness/config-foundation/infrastructure/presets/standard.json +24 -0
  47. package/scripts/harness/config-foundation/infrastructure/presets/strict.json +24 -0
  48. package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v2.schema.json +83 -0
  49. package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v3.schema.json +83 -0
  50. package/scripts/harness/harness-api/domain/value-objects/ci-check-result.ts +18 -4
  51. package/scripts/harness/harness-api/domain/value-objects/known-harness-commands.ts +4 -1
  52. package/scripts/harness/integrations/pre-commit.ts +169 -27
  53. package/scripts/harness/main.ts +276 -126
  54. package/scripts/harness/nyquist-validation/application/dto/generate-matrix-output.ts +11 -0
  55. package/scripts/harness/nyquist-validation/application/usecases/check-ac-coverage-gate-usecase.ts +6 -0
  56. package/scripts/harness/nyquist-validation/application/usecases/generate-requirement-test-matrix-usecase.ts +8 -4
  57. package/scripts/harness/nyquist-validation/domain/entities/story-mapping.ts +29 -2
  58. package/scripts/harness/nyquist-validation/domain/services/ac-coverage-gate-policy.ts +28 -0
  59. package/scripts/harness/nyquist-validation/infrastructure/adapters/markdown-requirement-source-adapter.ts +71 -4
  60. package/scripts/harness/nyquist-validation/infrastructure/schema/matrix-schema-loader.ts +4 -0
  61. package/scripts/harness/traceability-model/application/dto/changed-design-fragment-dto.ts +31 -0
  62. package/scripts/harness/traceability-model/application/dto/traceability-world-read-dto.ts +67 -0
  63. package/scripts/harness/traceability-model/application/facades/design-change-read-facade.ts +14 -0
  64. package/scripts/harness/traceability-model/application/facades/traceability-world-read-facade.ts +372 -0
  65. package/scripts/harness/traceability-model/application/ports/staged-design-change-source-port.ts +9 -0
  66. package/scripts/harness/traceability-model/application/ports/traceability-world-read-source-port.ts +61 -0
  67. package/scripts/harness/traceability-model/composition-root.ts +26 -5
  68. package/scripts/harness/traceability-model/index.ts +26 -6
  69. package/scripts/harness/traceability-model/infrastructure/adapters/file-system-traceability-world-read-adapter.ts +223 -0
  70. package/scripts/harness/traceability-model/infrastructure/adapters/git-staged-design-change-adapter.ts +155 -0
  71. package/scripts/harness/traceability-model/infrastructure/parsers/story-catalog-parser.ts +86 -8
  72. package/scripts/harness/validator-system/application/use-cases/run-l2-validators-usecase.ts +31 -0
  73. package/scripts/harness/validator-system/application/use-cases/run-l3-validators-usecase.ts +34 -0
  74. package/scripts/harness/validator-system/composition-root.ts +21 -6
  75. package/scripts/harness/validator-system/domain/ports/world-constraint-admission-policy-port.ts +28 -0
  76. package/scripts/harness/validator-system/domain/ports/world-constraint-rederivation-policy-port.ts +11 -0
  77. package/scripts/harness/validator-system/domain/services/design-change-declaration-policy.ts +75 -0
  78. package/scripts/harness/validator-system/domain/services/world-constraint-admission-service.ts +78 -0
  79. package/scripts/harness/validator-system/domain/services/world-constraint-rederivation-service.ts +76 -0
  80. package/scripts/harness/validator-system/domain/value-objects/validator-id.ts +4 -0
  81. package/scripts/harness/validator-system/infrastructure/adapters/harness-config-validator-config-adapter.ts +38 -16
  82. package/scripts/harness/validator-system/infrastructure/adapters/world-model-constraint-admission-adapter.ts +56 -0
  83. package/scripts/harness/validator-system/infrastructure/adapters/world-model-constraint-rederivation-adapter.ts +56 -0
  84. package/scripts/harness/world-model/application/dto/pinned-design-endpoint-dto.ts +19 -0
  85. package/scripts/harness/world-model/application/dto/world-inspection-dto.ts +43 -0
  86. package/scripts/harness/world-model/application/dto/world-obligation-report-dto.ts +54 -0
  87. package/scripts/harness/world-model/application/dto/world-resolved-config-input.ts +127 -0
  88. package/scripts/harness/world-model/application/dto/world-snapshot-root-dto.ts +8 -0
  89. package/scripts/harness/world-model/application/facades/pinned-design-endpoint-facade.ts +63 -0
  90. package/scripts/harness/world-model/application/facades/world-snapshot-root-facade.ts +19 -0
  91. package/scripts/harness/world-model/application/ports/obligation-report-writer-port.ts +7 -0
  92. package/scripts/harness/world-model/application/ports/world-control-declaration-repository-port.ts +57 -0
  93. package/scripts/harness/world-model/application/ports/world-fact-source-port.ts +17 -0
  94. package/scripts/harness/world-model/application/usecases/build-snapshot-use-case.ts +111 -0
  95. package/scripts/harness/world-model/application/usecases/derive-obligations-use-case.ts +155 -0
  96. package/scripts/harness/world-model/application/usecases/derive-world-obligations-use-case.ts +146 -0
  97. package/scripts/harness/world-model/application/usecases/inspect-world-use-case.ts +74 -0
  98. package/scripts/harness/world-model/application/usecases/pin-constraint-endpoint-use-case.ts +139 -0
  99. package/scripts/harness/world-model/composition-root.ts +247 -0
  100. package/scripts/harness/world-model/domain/entities/constraint-record.ts +148 -0
  101. package/scripts/harness/world-model/domain/entities/control-declarations.ts +274 -0
  102. package/scripts/harness/world-model/domain/entities/edge.ts +41 -0
  103. package/scripts/harness/world-model/domain/entities/extraction-diagnostic.ts +60 -0
  104. package/scripts/harness/world-model/domain/entities/snapshot.ts +75 -0
  105. package/scripts/harness/world-model/domain/entities/world-node.ts +210 -0
  106. package/scripts/harness/world-model/domain/ports/world-hashing-port.ts +8 -0
  107. package/scripts/harness/world-model/domain/services/canonical-json-serializer.ts +113 -0
  108. package/scripts/harness/world-model/domain/services/constraint-evaluator.ts +486 -0
  109. package/scripts/harness/world-model/domain/services/obligation-derivation-service.ts +199 -0
  110. package/scripts/harness/world-model/domain/services/policy-inputs-digest-deriver.ts +66 -0
  111. package/scripts/harness/world-model/domain/services/snapshot-root-deriver.ts +218 -0
  112. package/scripts/harness/world-model/domain/services/text-content-normalizer.ts +30 -0
  113. package/scripts/harness/world-model/domain/services/violation-fingerprint-deriver.ts +183 -0
  114. package/scripts/harness/world-model/domain/value-objects/artifact-kind.ts +58 -0
  115. package/scripts/harness/world-model/domain/value-objects/change-provenance.ts +137 -0
  116. package/scripts/harness/world-model/domain/value-objects/corpus-role.ts +57 -0
  117. package/scripts/harness/world-model/domain/value-objects/declared-key.ts +35 -0
  118. package/scripts/harness/world-model/domain/value-objects/evaluation-id.ts +41 -0
  119. package/scripts/harness/world-model/domain/value-objects/explicit-constraint-relation.ts +53 -0
  120. package/scripts/harness/world-model/domain/value-objects/explicit-node-alias.ts +30 -0
  121. package/scripts/harness/world-model/domain/value-objects/node-pin.ts +34 -0
  122. package/scripts/harness/world-model/domain/value-objects/path-key.ts +103 -0
  123. package/scripts/harness/world-model/domain/value-objects/sha256-digest.ts +39 -0
  124. package/scripts/harness/world-model/domain/value-objects/violation-fingerprint.ts +36 -0
  125. package/scripts/harness/world-model/domain/value-objects/wcr-rule-id.ts +59 -0
  126. package/scripts/harness/world-model/domain/value-objects/world-node-id.ts +222 -0
  127. package/scripts/harness/world-model/index.ts +33 -0
  128. package/scripts/harness/world-model/infrastructure/adapters/adr-fact-extractor.ts +27 -0
  129. package/scripts/harness/world-model/infrastructure/adapters/assembled-world-fact-source.ts +17 -0
  130. package/scripts/harness/world-model/infrastructure/adapters/attestation-fact-extractor.ts +323 -0
  131. package/scripts/harness/world-model/infrastructure/adapters/attestation-sha256-world-hashing-adapter.ts +15 -0
  132. package/scripts/harness/world-model/infrastructure/adapters/composite-design-fact-source.ts +23 -0
  133. package/scripts/harness/world-model/infrastructure/adapters/design-corpus-fact-extractor.ts +285 -0
  134. package/scripts/harness/world-model/infrastructure/adapters/design-fact-extraction.ts +63 -0
  135. package/scripts/harness/world-model/infrastructure/adapters/file-system-obligation-report-writer-adapter.ts +37 -0
  136. package/scripts/harness/world-model/infrastructure/adapters/file-system-world-control-repository-adapters.ts +332 -0
  137. package/scripts/harness/world-model/infrastructure/adapters/integrity-manifest-fact-extractor.ts +101 -0
  138. package/scripts/harness/world-model/infrastructure/adapters/json-fact-extractor-support.ts +151 -0
  139. package/scripts/harness/world-model/infrastructure/adapters/markdown-design-fact-extractor.ts +493 -0
  140. package/scripts/harness/world-model/infrastructure/adapters/matrix-fact-extractor.ts +283 -0
  141. package/scripts/harness/world-model/infrastructure/adapters/product-fact-extractor.ts +29 -0
  142. package/scripts/harness/world-model/infrastructure/adapters/proposal-fact-extractor.ts +29 -0
  143. package/scripts/harness/world-model/infrastructure/adapters/runtime-fact-extraction.ts +19 -0
  144. package/scripts/harness/world-model/infrastructure/adapters/source-metadata-fact-extractor.ts +22 -0
  145. package/scripts/harness/world-model/infrastructure/adapters/test-reference-source-fact-extractor.ts +22 -0
  146. package/scripts/harness/world-model/infrastructure/adapters/traceability-design-fact-adapter.ts +112 -0
  147. package/scripts/harness/world-model/infrastructure/adapters/traceability-world-read-facade-merger.ts +38 -0
  148. package/scripts/harness/world-model/infrastructure/adapters/type-script-source-fact-extractor.ts +236 -0
  149. package/scripts/harness/world-model/infrastructure/adapters/unit-fact-extractor.ts +71 -0
  150. package/scripts/harness/world-model/infrastructure/adapters/world-control-declaration-mapper.ts +383 -0
  151. package/scripts/harness/world-model/presentation/cli/world-command-support.ts +60 -0
  152. package/scripts/harness/world-model/presentation/cli/world-derive-command-handler.ts +109 -0
  153. package/scripts/harness/world-model/presentation/cli/world-inspect-command-handler.ts +194 -0
  154. package/scripts/harness/world-model/presentation/cli/world-pin-command-handler.ts +100 -0
@@ -76,6 +76,36 @@ The plan identifies target fields, managed artifacts, commands, validation, risk
76
76
  "format": "json",
77
77
  "outputDir": "reports"
78
78
  },
79
+ "world": {
80
+ "enabled": false,
81
+ "corpus": {
82
+ "productRoots": ["docs/product"],
83
+ "inceptionRoots": ["docs/inception"],
84
+ "adrRoots": ["docs/ADR"],
85
+ "sourceRoots": ["scripts/harness"],
86
+ "include": ["**/*"],
87
+ "exclude": []
88
+ },
89
+ "inputs": {
90
+ "matrixPath": ".harness/requirement-test-matrix.json",
91
+ "attestationPath": ".harness/attestation.json",
92
+ "integrityManifestPath": "phasegate.integrity.json"
93
+ },
94
+ "declarations": {
95
+ "constraintsPath": "phasegate.world-constraints.json",
96
+ "baselinePath": "phasegate.world-baseline.json",
97
+ "waiversPath": "phasegate.world-waivers.json",
98
+ "debtsPath": "phasegate.world-debts.json"
99
+ },
100
+ "output": {
101
+ "obligationReportPath": ".harness/world-obligations.json"
102
+ },
103
+ "sessionStart": {
104
+ "enabled": true,
105
+ "maxItems": 5,
106
+ "maxChars": 2000
107
+ }
108
+ },
79
109
  "preCommit": {
80
110
  "implementationExtensions": [".ts"]
81
111
  },
@@ -115,6 +145,8 @@ The `strict` preset also enables:
115
145
  - `bundleSizeLimit` -- 500 KB default cap
116
146
  - `deadCodeGC` -- automatic dead-code garbage collection
117
147
 
148
+ All three presets resolve the same World Model defaults with `world.enabled: false`. This switch controls future automatic L2/L3 and hook integration; it does not disable explicit `world:inspect`, `world:pin`, or `world:derive` commands. Enabling automatic World gates remains an explicit rollout decision. <!-- @work-item-id WI-300 -->
149
+
118
150
  ---
119
151
 
120
152
  ### Field Reference
@@ -216,6 +248,30 @@ Example for Python and TypeScript:
216
248
 
217
249
  The metadata validators read `@unit` and `@layer` with language-agnostic regular expressions, so Python `# @unit api` and Go `// @unit api` style comments are both valid as long as the file extension is included here.
218
250
 
251
+ #### `world`
252
+
253
+ <!-- @work-item-id WI-300 -->
254
+
255
+ `world` configures the World Model read corpus and reviewed control inputs. It is available in both v2 and v3 config schemas. Nested fields may be omitted and are resolved from the selected preset.
256
+
257
+ | Field | Default | Meaning |
258
+ |---|---|---|
259
+ | `enabled` | `false` | Enables future automatic validator/hook integration; explicit `world:*` commands remain available |
260
+ | `corpus.productRoots` | `["docs/product"]` | Canonical product corpus roots |
261
+ | `corpus.inceptionRoots` | `["docs/inception"]` | Proposal/delta corpus roots |
262
+ | `corpus.adrRoots` | `["docs/ADR"]` | ADR corpus roots |
263
+ | `corpus.sourceRoots` | `["scripts/harness"]` | Source/test corpus roots |
264
+ | `corpus.include` / `exclude` | `["**/*"]` / `[]` | Versioned selection patterns |
265
+ | `inputs.matrixPath` | `.harness/requirement-test-matrix.json` | Matrix owner projection input |
266
+ | `inputs.attestationPath` | `.harness/attestation.json` | Attestation evidence input |
267
+ | `inputs.integrityManifestPath` | `phasegate.integrity.json` | Integrity declaration input |
268
+ | `declarations.*Path` | `phasegate.world-{constraints,baseline,waivers,debts}.json` | Git-reviewed external declarations |
269
+ | `output.obligationReportPath` | `.harness/world-obligations.json` | Generated, non-authoritative report path |
270
+ | `sessionStart.enabled` | `true` | Allows the bounded World summary when session integration is installed |
271
+ | `sessionStart.maxItems` / `maxChars` | `5` / `2000` | Summary caps; valid ranges are 1–20 and 1–8000 |
272
+
273
+ All paths are project-relative POSIX paths. Absolute paths, backslashes, `.` / `..` segments, cross-role corpus root overlap, and case-fold collisions are rejected. When the matching World field is omitted, legacy `paths.designDocs`, `paths.inceptionDocs`, and `layers.L3.requirementMatrixPath` remain compatible inputs. A present but invalid `world` section fails closed; it is never replaced with defaults silently.
274
+
219
275
  #### `phase2Extensions`
220
276
 
221
277
  <!-- @work-item-id WI-170 -->
@@ -7,8 +7,10 @@
7
7
  # 実行内容:
8
8
  # 1. bypass:audit — bypass trailer / evidence 監査
9
9
  # 2. phasegate lint — L1 Biome AST ルール検証
10
- # 3. phasegate:ci-check L3 CI バリデータ(security / performance / coverage / nyquist)
11
- # 4. 失敗時: PR にエラーサマリーをコメント
10
+ # 3. requirement-test matrix をcurrent testsから再生成
11
+ # 4. world.enabled=true の場合、world:derive を2回実行してbyte一致を検証
12
+ # 5. phasegate:ci-check — L3 CI バリデータ(security / performance / coverage / nyquist / World)
13
+ # 6. 失敗時: PR にエラーサマリーをコメント
12
14
 
13
15
  name: AIDLC Quality Gate
14
16
 
@@ -77,6 +79,44 @@ jobs:
77
79
  echo "exit_code=$EXIT_CODE" >> $GITHUB_OUTPUT
78
80
  exit $EXIT_CODE
79
81
 
82
+ # L3-004 consumes the generated requirement-test matrix. Always rebuild it
83
+ # from this checkout instead of trusting a stale or persisted artifact.
84
+ - name: Generate requirement-test matrix
85
+ if: always()
86
+ run: npx phasegate phasegate:generate-matrix
87
+
88
+ # Automatic World integration is selected only by an explicit true value.
89
+ # Missing/false keeps existing projects compatible; invalid JSON is a hard
90
+ # configuration error and must not be laundered into a disabled result.
91
+ - name: Detect World Model enablement
92
+ id: world_config
93
+ if: always()
94
+ shell: bash
95
+ run: |
96
+ set -euo pipefail
97
+ ENABLED=$(node --input-type=module <<'NODE'
98
+ import { existsSync, readFileSync } from 'node:fs';
99
+ if (!existsSync('phasegate.config.json')) {
100
+ process.stdout.write('false');
101
+ } else {
102
+ const config = JSON.parse(readFileSync('phasegate.config.json', 'utf8'));
103
+ process.stdout.write(config.world?.enabled === true ? 'true' : 'false');
104
+ }
105
+ NODE
106
+ )
107
+ echo "enabled=$ENABLED" >> "$GITHUB_OUTPUT"
108
+
109
+ - name: World derive determinism
110
+ if: steps.world_config.outputs.enabled == 'true'
111
+ shell: bash
112
+ run: |
113
+ set -euo pipefail
114
+ FIRST="$RUNNER_TEMP/phasegate-world-first.json"
115
+ SECOND="$RUNNER_TEMP/phasegate-world-second.json"
116
+ npx phasegate world:derive --json > "$FIRST"
117
+ npx phasegate world:derive --json > "$SECOND"
118
+ cmp --silent "$FIRST" "$SECOND"
119
+
80
120
  # L3: CI バリデータ(security / performance / coverage / nyquist)
81
121
  - name: L3 CI Check
82
122
  id: ci_check
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "phasegate",
3
- "version": "0.229.0",
3
+ "version": "0.264.0",
4
4
  "packageManager": "pnpm@10.30.1",
5
5
  "description": "Phasegate — AI-agnostic quality defense toolkit. Enforces structural integrity between design intent and code.",
6
6
  "license": "MIT",
@@ -0,0 +1,30 @@
1
+ // @unit agent-integration
2
+ // @layer application
3
+ // @work-item-id WI-304
4
+
5
+ export type OpenWorldObligationClassification =
6
+ | "new-structural"
7
+ | "invalid-declaration"
8
+ | "expired-waiver"
9
+ | "policy-diagnostic"
10
+ | "cleanup-required"
11
+ | "waived";
12
+
13
+ export interface OpenWorldObligationContextItemDto {
14
+ readonly kind: "structural" | "cleanup-required" | "policy-diagnostic";
15
+ readonly classification: OpenWorldObligationClassification;
16
+ readonly ruleId: string | null;
17
+ readonly constraintId: string | null;
18
+ readonly violationFingerprint: string | null;
19
+ readonly subjectId: string | null;
20
+ readonly policyCode?: string | null;
21
+ }
22
+
23
+ export type OpenWorldObligationsContextDto =
24
+ | { readonly status: "disabled" }
25
+ | { readonly status: "unavailable" }
26
+ | {
27
+ readonly status: "available";
28
+ readonly entries: readonly OpenWorldObligationContextItemDto[];
29
+ readonly adoptedLegacyCount: number;
30
+ };
@@ -0,0 +1,30 @@
1
+ // @unit agent-integration
2
+ // @layer application
3
+ // @work-item-id WI-304
4
+
5
+ export type QueriedWorldObligationClassification =
6
+ | "adopted-legacy"
7
+ | "new-structural"
8
+ | "invalid-declaration"
9
+ | "expired-waiver"
10
+ | "policy-diagnostic"
11
+ | "cleanup-required"
12
+ | "waived";
13
+
14
+ export interface QueriedWorldObligationEntry {
15
+ readonly kind: "structural" | "cleanup-required" | "policy-diagnostic";
16
+ readonly classification: QueriedWorldObligationClassification;
17
+ readonly ruleId: string | null;
18
+ readonly constraintId: string | null;
19
+ readonly violationFingerprint: string | null;
20
+ readonly subjectId?: string | null;
21
+ readonly policyCode?: string | null;
22
+ }
23
+
24
+ export type WorldObligationsQueryResult =
25
+ | { readonly status: "available"; readonly entries: readonly QueriedWorldObligationEntry[] }
26
+ | { readonly status: "unavailable" };
27
+
28
+ export interface WorldObligationsQueryPort {
29
+ query(): Promise<WorldObligationsQueryResult>;
30
+ }
@@ -0,0 +1,66 @@
1
+ // @unit agent-integration
2
+ // @layer application
3
+ // @work-item-id WI-304
4
+
5
+ import type {
6
+ OpenWorldObligationContextItemDto,
7
+ OpenWorldObligationsContextDto,
8
+ } from "../dto/open-world-obligations-context-dto.js";
9
+ import type { QueriedWorldObligationEntry, WorldObligationsQueryPort } from "../ports/world-obligations-query-port.js";
10
+
11
+ interface Dependencies {
12
+ readonly worldObligationsQueryPort: WorldObligationsQueryPort;
13
+ }
14
+
15
+ const priority = (entry: OpenWorldObligationContextItemDto): number => {
16
+ if (entry.classification === "new-structural" || entry.classification === "invalid-declaration") return 0;
17
+ if (entry.classification === "expired-waiver" || entry.classification === "policy-diagnostic") return 1;
18
+ if (entry.classification === "cleanup-required") return 2;
19
+ return 3;
20
+ };
21
+
22
+ const stableKey = (entry: OpenWorldObligationContextItemDto): string =>
23
+ [
24
+ priority(entry).toString().padStart(2, "0"),
25
+ entry.ruleId ?? "",
26
+ entry.constraintId ?? "",
27
+ entry.violationFingerprint ?? "",
28
+ entry.subjectId ?? "",
29
+ entry.policyCode ?? "",
30
+ ].join("\u0000");
31
+
32
+ type DisplayableQueriedEntry = QueriedWorldObligationEntry & {
33
+ readonly classification: Exclude<QueriedWorldObligationEntry["classification"], "adopted-legacy">;
34
+ };
35
+
36
+ const isDisplayable = (entry: QueriedWorldObligationEntry): entry is DisplayableQueriedEntry =>
37
+ entry.classification !== "adopted-legacy";
38
+
39
+ const toContextItem = (entry: DisplayableQueriedEntry): OpenWorldObligationContextItemDto => ({
40
+ kind: entry.kind,
41
+ classification: entry.classification,
42
+ ruleId: entry.ruleId,
43
+ constraintId: entry.constraintId,
44
+ violationFingerprint: entry.violationFingerprint,
45
+ subjectId: entry.subjectId ?? null,
46
+ policyCode: entry.policyCode ?? null,
47
+ });
48
+
49
+ export class GetOpenWorldObligationsContextUseCase {
50
+ constructor(private readonly dependencies: Dependencies) {}
51
+
52
+ async execute(input: { readonly enabled: boolean }): Promise<OpenWorldObligationsContextDto> {
53
+ if (!input.enabled) return { status: "disabled" };
54
+
55
+ const result = await this.dependencies.worldObligationsQueryPort.query();
56
+ if (result.status === "unavailable") return { status: "unavailable" };
57
+
58
+ const adoptedLegacyCount = result.entries.filter((entry) => entry.classification === "adopted-legacy").length;
59
+ const entries = result.entries
60
+ .filter(isDisplayable)
61
+ .map(toContextItem)
62
+ .sort((left, right) => (stableKey(left) < stableKey(right) ? -1 : stableKey(left) > stableKey(right) ? 1 : 0));
63
+
64
+ return { status: "available", entries: Object.freeze(entries), adoptedLegacyCount };
65
+ }
66
+ }
@@ -0,0 +1,58 @@
1
+ // @unit agent-integration
2
+ // @layer infrastructure
3
+ // @work-item-id WI-304
4
+
5
+ import { createWorldModelModule, type WorldResolvedConfigInput } from "../../../world-model/index.js";
6
+ import type {
7
+ QueriedWorldObligationEntry,
8
+ WorldObligationsQueryPort,
9
+ WorldObligationsQueryResult,
10
+ } from "../../application/ports/world-obligations-query-port.js";
11
+
12
+ interface Options {
13
+ readonly rootDir: string;
14
+ readonly resolvedConfig?: WorldResolvedConfigInput;
15
+ }
16
+
17
+ export class WorldModelOpenObligationsQueryAdapter implements WorldObligationsQueryPort {
18
+ constructor(private readonly options: Options) {}
19
+
20
+ async query(): Promise<WorldObligationsQueryResult> {
21
+ try {
22
+ const world = createWorldModelModule(this.options);
23
+ const derived = await world.deriveWorldObligationsUseCase.execute({ writeReport: false });
24
+ if (derived.status !== "derived") return { status: "unavailable" };
25
+
26
+ const report = derived.result.report;
27
+ const entries: QueriedWorldObligationEntry[] = [
28
+ ...report.structuralObligations.map((entry) => ({
29
+ kind: "structural" as const,
30
+ classification: entry.classification,
31
+ ruleId: entry.ruleId,
32
+ constraintId: entry.constraintId,
33
+ violationFingerprint: entry.violationFingerprint,
34
+ })),
35
+ ...report.repaidBaselineEntries.map((entry) => ({
36
+ kind: "cleanup-required" as const,
37
+ classification: "cleanup-required" as const,
38
+ ruleId: entry.ruleId,
39
+ constraintId: entry.constraintId,
40
+ violationFingerprint: entry.violationFingerprint,
41
+ })),
42
+ ...report.policyDiagnostics.map((entry) => ({
43
+ kind: "policy-diagnostic" as const,
44
+ classification:
45
+ entry.code === "expired-waiver" ? ("expired-waiver" as const) : ("policy-diagnostic" as const),
46
+ ruleId: null,
47
+ constraintId: null,
48
+ violationFingerprint: null,
49
+ subjectId: entry.subjectId,
50
+ policyCode: entry.code,
51
+ })),
52
+ ];
53
+ return { status: "available", entries: Object.freeze(entries) };
54
+ } catch {
55
+ return { status: "unavailable" };
56
+ }
57
+ }
58
+ }
@@ -1,13 +1,18 @@
1
1
  // @unit agent-integration
2
2
  // @layer presentation
3
+ // @work-item-id WI-304
3
4
 
4
5
  import { buildCiGovernance } from "../../ci-governance/composition-root.js";
6
+ import { createConfigFoundationModule, toWorldModelConfig } from "../../config-foundation/index.js";
7
+ import { GetOpenWorldObligationsContextUseCase } from "../application/usecases/get-open-world-obligations-context-usecase.js";
8
+ import { WorldModelOpenObligationsQueryAdapter } from "../infrastructure/adapters/world-model-open-obligations-query-adapter.js";
5
9
  import {
6
10
  buildIntegrityUnverifiableWarning,
7
11
  buildIntegrityWarning,
8
12
  buildSessionStartContext,
9
13
  collectPhasegateStatus,
10
14
  } from "./phasegate-status-context.js";
15
+ import { buildWorldObligationsSessionContext } from "./world-obligations-session-context.js";
11
16
 
12
17
  async function readStdin(): Promise<string> {
13
18
  const chunks: Buffer[] = [];
@@ -36,6 +41,36 @@ async function buildIntegrityContext(cwd: string): Promise<string | null> {
36
41
  }
37
42
  }
38
43
 
44
+ /**
45
+ * World の保存済み report は読まず、公開 facade の pure derive から現在の open obligation を要約する。
46
+ * config/derive の失敗は固定 warning へ縮退し、repository 由来のエラー文字列は中継しない。
47
+ */
48
+ async function buildWorldContext(cwd: string): Promise<string | null> {
49
+ try {
50
+ const configModule = createConfigFoundationModule();
51
+ const resolved = await configModule.usecases.loadResolvedConfigUseCase.execute();
52
+ const worldConfig = toWorldModelConfig(resolved.config);
53
+ if (worldConfig === undefined) return null;
54
+
55
+ const contextUseCase = new GetOpenWorldObligationsContextUseCase({
56
+ worldObligationsQueryPort: new WorldModelOpenObligationsQueryAdapter({
57
+ rootDir: cwd,
58
+ resolvedConfig: worldConfig,
59
+ }),
60
+ });
61
+ const context = await contextUseCase.execute({
62
+ enabled: worldConfig.enabled && worldConfig.sessionStart.enabled,
63
+ });
64
+ return buildWorldObligationsSessionContext(context, {
65
+ maxItems: worldConfig.sessionStart.maxItems,
66
+ maxChars: worldConfig.sessionStart.maxChars,
67
+ });
68
+ } catch (error) {
69
+ if (error instanceof Error && error.name === "ConfigNotFoundError") return null;
70
+ return buildWorldObligationsSessionContext({ status: "unavailable" }, { maxItems: 5, maxChars: 2000 });
71
+ }
72
+ }
73
+
39
74
  async function main(): Promise<void> {
40
75
  try {
41
76
  await readStdin();
@@ -46,7 +81,8 @@ async function main(): Promise<void> {
46
81
  const status = await collectPhasegateStatus(process.cwd());
47
82
  const baseContext = buildSessionStartContext(status);
48
83
  const integrityContext = await buildIntegrityContext(process.cwd());
49
- const additionalContext = integrityContext === null ? baseContext : `${integrityContext}\n\n${baseContext}`;
84
+ const worldContext = await buildWorldContext(process.cwd());
85
+ const additionalContext = [integrityContext, worldContext, baseContext].filter((item) => item !== null).join("\n\n");
50
86
 
51
87
  const output = {
52
88
  hookSpecificOutput: {
@@ -0,0 +1,60 @@
1
+ // @unit agent-integration
2
+ // @layer presentation
3
+ // @work-item-id WI-304
4
+
5
+ import type {
6
+ OpenWorldObligationContextItemDto,
7
+ OpenWorldObligationsContextDto,
8
+ } from "../application/dto/open-world-obligations-context-dto.js";
9
+
10
+ const UNAVAILABLE_WARNING =
11
+ "⚠ World obligations unavailable at SessionStart; continuing fail-open. Run phasegate world:derive.";
12
+ const HEADER = "## World open obligations (SessionStart)";
13
+ const HARD_MAX_ITEMS = 5;
14
+ const HARD_MAX_CHARS = 2000;
15
+
16
+ export const countUnicodeScalars = (value: string): number => [...value].length;
17
+
18
+ const renderEntry = (entry: OpenWorldObligationContextItemDto): string => {
19
+ if (entry.kind === "policy-diagnostic") {
20
+ return `- [BLOCKING ${entry.classification}] subject=${entry.subjectId ?? "unknown"}`;
21
+ }
22
+ const label =
23
+ entry.classification === "cleanup-required"
24
+ ? "CLEANUP"
25
+ : entry.classification === "waived"
26
+ ? "WAIVED"
27
+ : `BLOCKING ${entry.classification}`;
28
+ return `- [${label}] ${entry.ruleId ?? "unknown-rule"} constraint=${entry.constraintId ?? "implicit"} fingerprint=${entry.violationFingerprint ?? "none"}`;
29
+ };
30
+
31
+ const renderAvailable = (
32
+ input: Extract<OpenWorldObligationsContextDto, { status: "available" }>,
33
+ shown: number,
34
+ ): string => {
35
+ const lines = [HEADER, ...input.entries.slice(0, shown).map(renderEntry)];
36
+ if (input.adoptedLegacyCount > 0) lines.push(`- Adopted legacy: ${input.adoptedLegacyCount} (summary only)`);
37
+ if (input.entries.length === 0 && input.adoptedLegacyCount === 0) lines.push("- None.");
38
+ const omitted = input.entries.length - shown;
39
+ if (omitted > 0) lines.push(`... ${omitted} more; run phasegate world:derive`);
40
+ return lines.join("\n");
41
+ };
42
+
43
+ export function buildWorldObligationsSessionContext(
44
+ input: OpenWorldObligationsContextDto,
45
+ limits: { readonly maxItems: number; readonly maxChars: number },
46
+ ): string | null {
47
+ const maxItems = Math.min(limits.maxItems, HARD_MAX_ITEMS);
48
+ const maxChars = Math.min(limits.maxChars, HARD_MAX_CHARS);
49
+ if (input.status === "disabled") return null;
50
+ if (input.status === "unavailable") {
51
+ return countUnicodeScalars(UNAVAILABLE_WARNING) <= maxChars ? UNAVAILABLE_WARNING : null;
52
+ }
53
+
54
+ const maximumShown = Math.min(input.entries.length, maxItems);
55
+ for (let shown = maximumShown; shown >= 0; shown -= 1) {
56
+ const candidate = renderAvailable(input, shown);
57
+ if (countUnicodeScalars(candidate) <= maxChars) return candidate;
58
+ }
59
+ return null;
60
+ }
@@ -1,8 +1,9 @@
1
1
  // @unit attestation
2
2
  // @layer application
3
+ // @work-item-id WI-306
3
4
 
4
5
  /**
5
- * record format `phasegate-attestation/v1` plain object 表現(logical_design §1.4.3)。
6
+ * versioned record formatのplain object表現(logical_design §1.4.3 / WI-306)。
6
7
  * ファイル入出力・stdout エコー・mapper の境界で用いる契約型。VO はプリミティブ展開する。
7
8
  */
8
9
  export interface AttestationDocumentValidatorOutcome {
@@ -23,9 +24,7 @@ export interface AttestationDocumentGranularityClaim {
23
24
  readonly knownLimitations: string[];
24
25
  }
25
26
 
26
- export interface AttestationDocument {
27
- readonly schemaVersion: "phasegate-attestation/v1";
28
- readonly predicateType: string;
27
+ interface AttestationDocumentBase {
29
28
  readonly subject: {
30
29
  readonly command: string;
31
30
  readonly gateResult: "pass" | "fail";
@@ -54,3 +53,17 @@ export interface AttestationDocument {
54
53
  readonly value: string | null;
55
54
  };
56
55
  }
56
+
57
+ export interface AttestationDocumentV1 extends AttestationDocumentBase {
58
+ readonly schemaVersion: "phasegate-attestation/v1";
59
+ readonly predicateType: "https://phasegate.dev/attestation/gate-run/v1";
60
+ }
61
+
62
+ export interface AttestationDocumentV2 extends AttestationDocumentBase {
63
+ readonly schemaVersion: "phasegate-attestation/v2";
64
+ readonly predicateType: "https://phasegate.dev/attestation/gate-run/v2";
65
+ /** 実行時のWorld canonical corpusRoot。個別fragment digestは保持しない。 */
66
+ readonly worldSnapshotRoot: string;
67
+ }
68
+
69
+ export type AttestationDocument = AttestationDocumentV1 | AttestationDocumentV2;
@@ -1,5 +1,6 @@
1
1
  // @unit attestation
2
2
  // @layer application
3
+ // @work-item-id WI-306
3
4
 
4
5
  import {
5
6
  AttestationRecord,
@@ -44,15 +45,20 @@ function requireBoolean(v: unknown, field: string): boolean {
44
45
  return v;
45
46
  }
46
47
 
48
+ function rejectUnknownFields(value: Record<string, unknown>, allowed: readonly string[], field: string): void {
49
+ const unsupported = Object.keys(value).filter((key) => !allowed.includes(key));
50
+ if (unsupported.length > 0) {
51
+ throw new MalformedAttestationError(`${field} contains unsupported fields: ${unsupported.sort().join(", ")}`);
52
+ }
53
+ }
54
+
47
55
  /**
48
56
  * AttestationRecord(domain)↔ AttestationDocument(application DTO)の双方向変換。
49
57
  */
50
58
  export class AttestationRecordMapper {
51
59
  /** 集約を plain object へ射影(VO をプリミティブ展開)。 */
52
60
  toDocument(record: AttestationRecord): AttestationDocument {
53
- return {
54
- schemaVersion: record.schemaVersion as "phasegate-attestation/v1",
55
- predicateType: record.predicateType,
61
+ const common = {
56
62
  subject: {
57
63
  command: record.subject.command,
58
64
  gateResult: record.subject.gateResult,
@@ -63,7 +69,7 @@ export class AttestationRecordMapper {
63
69
  })),
64
70
  },
65
71
  inputs: {
66
- digestAlgorithm: "sha256",
72
+ digestAlgorithm: "sha256" as const,
67
73
  sources: record.inputs.sources.map((s) => ({ path: s.path, digest: s.digest.value })),
68
74
  inputDigest: record.inputs.inputDigest.value,
69
75
  },
@@ -89,6 +95,19 @@ export class AttestationRecordMapper {
89
95
  value: record.signature.value,
90
96
  },
91
97
  };
98
+ if (record.worldSnapshotRoot !== null) {
99
+ return {
100
+ ...common,
101
+ schemaVersion: "phasegate-attestation/v2",
102
+ predicateType: "https://phasegate.dev/attestation/gate-run/v2",
103
+ worldSnapshotRoot: record.worldSnapshotRoot.value,
104
+ };
105
+ }
106
+ return {
107
+ ...common,
108
+ schemaVersion: "phasegate-attestation/v1",
109
+ predicateType: "https://phasegate.dev/attestation/gate-run/v1",
110
+ };
92
111
  }
93
112
 
94
113
  /**
@@ -102,6 +121,37 @@ export class AttestationRecordMapper {
102
121
 
103
122
  const schemaVersion = requireString(doc.schemaVersion, "schemaVersion");
104
123
  const predicateType = requireString(doc.predicateType, "predicateType");
124
+ let worldSnapshotRoot: Digest | undefined;
125
+ if (schemaVersion === "phasegate-attestation/v1") {
126
+ if (predicateType !== "https://phasegate.dev/attestation/gate-run/v1") {
127
+ throw new MalformedAttestationError("v1 predicateType does not match schemaVersion");
128
+ }
129
+ if (doc.worldSnapshotRoot !== undefined) {
130
+ throw new MalformedAttestationError("v1 must not contain worldSnapshotRoot");
131
+ }
132
+ } else if (schemaVersion === "phasegate-attestation/v2") {
133
+ if (predicateType !== "https://phasegate.dev/attestation/gate-run/v2") {
134
+ throw new MalformedAttestationError("v2 predicateType does not match schemaVersion");
135
+ }
136
+ worldSnapshotRoot = this.toDigest(doc.worldSnapshotRoot, "worldSnapshotRoot");
137
+ rejectUnknownFields(
138
+ doc,
139
+ [
140
+ "schemaVersion",
141
+ "predicateType",
142
+ "subject",
143
+ "inputs",
144
+ "granularity",
145
+ "acBoundScope",
146
+ "worldSnapshotRoot",
147
+ "metadata",
148
+ "signature",
149
+ ],
150
+ "v2 attestation",
151
+ );
152
+ } else {
153
+ throw new MalformedAttestationError(`unsupported schemaVersion: ${schemaVersion}`);
154
+ }
105
155
 
106
156
  const subject = doc.subject;
107
157
  if (!isObject(subject)) {
@@ -185,6 +235,7 @@ export class AttestationRecordMapper {
185
235
  },
186
236
  signature: this.toSignatureBlock(mode, attestationDigest, signature),
187
237
  acBoundScope,
238
+ ...(worldSnapshotRoot === undefined ? {} : { worldSnapshotRoot }),
188
239
  };
189
240
 
190
241
  // verify 用の再構築: INV-1/INV-3 は verify usecase が機械的チェックとして検出するため
@@ -0,0 +1,25 @@
1
+ // @unit attestation
2
+ // @layer application
3
+ // @work-item-id WI-286
4
+
5
+ /**
6
+ * Unit境界を越えて受け渡すplain SHA-256 digest。
7
+ *
8
+ * 64桁lowercase hexのruntime invariantはproviderと各consumer-local VOが検証する。
9
+ * attestation domainのDigestはこのpublic contractへ露出しない。
10
+ */
11
+ export type Sha256DigestString = `sha256:${string}`;
12
+
13
+ /**
14
+ * Raw bytesをSHA-256するattestation public capability。
15
+ */
16
+ export interface Sha256Capability {
17
+ hashBytes(bytes: Uint8Array): Sha256DigestString;
18
+ }
19
+
20
+ /**
21
+ * Unicode normalizationを行わず、TextEncoderのUTF-8 bytesをpublic capabilityへ渡す。
22
+ */
23
+ export function hashUtf8(capability: Sha256Capability, text: string): Sha256DigestString {
24
+ return capability.hashBytes(new TextEncoder().encode(text));
25
+ }
@@ -0,0 +1,10 @@
1
+ // @unit attestation
2
+ // @layer application
3
+ // @work-item-id WI-306
4
+
5
+ /**
6
+ * attestationが所有するconsumer port。providerのWorld domain型を公開しない。
7
+ */
8
+ export interface WorldSnapshotRootProvider {
9
+ getWorldSnapshotRoot(): Promise<string>;
10
+ }