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
@@ -0,0 +1,148 @@
1
+ // @unit world-model
2
+ // @layer domain
3
+ // @work-item-id WI-293
4
+
5
+ import type { CanonicalJsonObject } from "../services/canonical-json-serializer.js";
6
+ import type { NodePin } from "../value-objects/node-pin.js";
7
+ import type { WcrRuleId } from "../value-objects/wcr-rule-id.js";
8
+ import type { WorldNodeId } from "../value-objects/world-node-id.js";
9
+
10
+ export type ConstraintFactType = "references" | "depends-on" | "refines" | "content-equals";
11
+
12
+ const FACT_TYPES: readonly ConstraintFactType[] = ["references", "depends-on", "refines", "content-equals"];
13
+ const SCHEMA_VERSION = "phasegate-world-constraints/v1";
14
+ const compareStrings = (left: string, right: string): number => (left < right ? -1 : left > right ? 1 : 0);
15
+
16
+ export interface ConstraintRecordProps {
17
+ readonly constraintId: WorldNodeId;
18
+ readonly schemaVersion: string;
19
+ readonly factType: ConstraintFactType;
20
+ readonly claimant: NodePin;
21
+ readonly premise: NodePin;
22
+ readonly applicableRuleIds: readonly WcrRuleId[];
23
+ readonly declarationArtifactId: WorldNodeId;
24
+ readonly declarationLocator: string;
25
+ }
26
+
27
+ export class InvalidConstraintRecordError extends Error {
28
+ constructor(message: string) {
29
+ super(`Invalid World constraint record: ${message}`);
30
+ this.name = "InvalidConstraintRecordError";
31
+ }
32
+ }
33
+
34
+ export class ConstraintRecord {
35
+ readonly constraintId: WorldNodeId;
36
+ readonly schemaVersion: string;
37
+ readonly factType: ConstraintFactType;
38
+ readonly claimant: NodePin;
39
+ readonly premise: NodePin;
40
+ readonly applicableRuleIds: readonly WcrRuleId[];
41
+ readonly declarationArtifactId: WorldNodeId;
42
+ readonly declarationLocator: string;
43
+
44
+ private constructor(props: ConstraintRecordProps, ruleIds: readonly WcrRuleId[]) {
45
+ this.constraintId = props.constraintId;
46
+ this.schemaVersion = props.schemaVersion;
47
+ this.factType = props.factType;
48
+ this.claimant = props.claimant;
49
+ this.premise = props.premise;
50
+ this.applicableRuleIds = ruleIds;
51
+ this.declarationArtifactId = props.declarationArtifactId;
52
+ this.declarationLocator = props.declarationLocator;
53
+ Object.freeze(this);
54
+ }
55
+
56
+ static create(props: ConstraintRecordProps): ConstraintRecord {
57
+ if (props.constraintId.nodeType !== "constraint") {
58
+ throw new InvalidConstraintRecordError("constraintId must be a constraint node ID");
59
+ }
60
+ if (props.schemaVersion !== SCHEMA_VERSION) {
61
+ throw new InvalidConstraintRecordError(`unsupported schemaVersion "${props.schemaVersion}"`);
62
+ }
63
+ if (!FACT_TYPES.includes(props.factType)) {
64
+ throw new InvalidConstraintRecordError(`unsupported factType "${props.factType}"`);
65
+ }
66
+ if (props.applicableRuleIds.length === 0) {
67
+ throw new InvalidConstraintRecordError("applicableRuleIds must be non-empty");
68
+ }
69
+ const sortedRuleIds = [...props.applicableRuleIds].sort((left, right) =>
70
+ compareStrings(left.toString(), right.toString()),
71
+ );
72
+ const uniqueRuleIds = new Set(sortedRuleIds.map((ruleId) => ruleId.toString()));
73
+ if (uniqueRuleIds.size !== sortedRuleIds.length) {
74
+ throw new InvalidConstraintRecordError("applicableRuleIds must be unique");
75
+ }
76
+ if (
77
+ props.declarationArtifactId.nodeType !== "artifact" ||
78
+ !props.declarationArtifactId.toString().startsWith("pgw:v1:artifact:external-declaration:external:")
79
+ ) {
80
+ throw new InvalidConstraintRecordError("declarationArtifactId must identify an external declaration artifact");
81
+ }
82
+ if (props.declarationLocator.trim().length === 0) {
83
+ throw new InvalidConstraintRecordError("declarationLocator must be non-empty");
84
+ }
85
+ return new ConstraintRecord(props, Object.freeze(sortedRuleIds));
86
+ }
87
+
88
+ applies(ruleId: string): boolean {
89
+ return this.applicableRuleIds.some((candidate) => candidate.toString() === ruleId);
90
+ }
91
+
92
+ toCanonicalValue(): CanonicalJsonObject {
93
+ return {
94
+ applicableRuleIds: this.applicableRuleIds.map((ruleId) => ruleId.toString()),
95
+ claimant: this.claimant.toCanonicalValue(),
96
+ constraintId: this.constraintId.toString(),
97
+ declarationArtifactId: this.declarationArtifactId.toString(),
98
+ declarationLocator: this.declarationLocator,
99
+ factType: this.factType,
100
+ premise: this.premise.toCanonicalValue(),
101
+ schemaVersion: this.schemaVersion,
102
+ };
103
+ }
104
+ }
105
+
106
+ export interface MalformedConstraintDeclarationProps {
107
+ readonly declaredConstraintId?: string;
108
+ readonly declarationArtifactId: WorldNodeId;
109
+ readonly declarationLocator: string;
110
+ readonly reasons: readonly string[];
111
+ }
112
+
113
+ export class MalformedConstraintDeclaration {
114
+ readonly declaredConstraintId: string | null;
115
+ readonly declarationArtifactId: WorldNodeId;
116
+ readonly declarationLocator: string;
117
+ readonly reasons: readonly string[];
118
+
119
+ private constructor(props: MalformedConstraintDeclarationProps) {
120
+ this.declaredConstraintId = props.declaredConstraintId ?? null;
121
+ this.declarationArtifactId = props.declarationArtifactId;
122
+ this.declarationLocator = props.declarationLocator;
123
+ this.reasons = Object.freeze([...props.reasons].sort());
124
+ Object.freeze(this);
125
+ }
126
+
127
+ static create(props: MalformedConstraintDeclarationProps): MalformedConstraintDeclaration {
128
+ if (
129
+ props.declarationArtifactId.nodeType !== "artifact" ||
130
+ !props.declarationArtifactId.toString().startsWith("pgw:v1:artifact:external-declaration:external:")
131
+ ) {
132
+ throw new InvalidConstraintRecordError("malformed declaration provenance must be an external declaration");
133
+ }
134
+ if (props.declarationLocator.trim().length === 0 || props.reasons.length === 0) {
135
+ throw new InvalidConstraintRecordError("malformed declaration requires locator and reasons");
136
+ }
137
+ return new MalformedConstraintDeclaration(props);
138
+ }
139
+
140
+ toCanonicalValue(): CanonicalJsonObject {
141
+ return {
142
+ declarationArtifactId: this.declarationArtifactId.toString(),
143
+ declarationLocator: this.declarationLocator,
144
+ declaredConstraintId: this.declaredConstraintId,
145
+ reasons: this.reasons,
146
+ };
147
+ }
148
+ }
@@ -0,0 +1,274 @@
1
+ // @unit world-model
2
+ // @layer domain
3
+ // @work-item-id WI-294
4
+
5
+ import type { CanonicalJsonObject } from "../services/canonical-json-serializer.js";
6
+ import type { EvaluationId } from "../value-objects/evaluation-id.js";
7
+ import type { Sha256Digest } from "../value-objects/sha256-digest.js";
8
+ import type { ViolationFingerprint } from "../value-objects/violation-fingerprint.js";
9
+ import type { WcrRuleId } from "../value-objects/wcr-rule-id.js";
10
+ import type { WorldNodeId } from "../value-objects/world-node-id.js";
11
+
12
+ const compareStrings = (left: string, right: string): number => (left < right ? -1 : left > right ? 1 : 0);
13
+ const WORK_ITEM_PATTERN = /^WI-\d+$/;
14
+ const WAIVER_ID_PATTERN = /^pgw:v1:waiver:[a-z][a-z0-9]*(?:[._-][a-z0-9]+)*$/;
15
+ const DEBT_ID_PATTERN = /^pgw:v1:semantic-debt:[a-z][a-z0-9]*(?:[._-][a-z0-9]+)*$/;
16
+ const UNIT_ID_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
17
+
18
+ export class InvalidControlDeclarationDomainError extends Error {
19
+ constructor(message: string) {
20
+ super(`Invalid World control declaration: ${message}`);
21
+ this.name = "InvalidControlDeclarationDomainError";
22
+ }
23
+ }
24
+
25
+ const requireNonEmpty = (value: string, field: string): string => {
26
+ if (value.trim().length === 0) throw new InvalidControlDeclarationDomainError(`${field} must be non-empty`);
27
+ return value;
28
+ };
29
+
30
+ const requireWorkItemId = (value: string, field: string): string => {
31
+ if (!WORK_ITEM_PATTERN.test(value)) throw new InvalidControlDeclarationDomainError(`${field} must be WI-<digits>`);
32
+ return value;
33
+ };
34
+
35
+ const requireCalendarDate = (value: string): string => {
36
+ const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
37
+ if (!match) throw new InvalidControlDeclarationDomainError("expiresOn must be YYYY-MM-DD");
38
+ const year = Number(match[1]);
39
+ const month = Number(match[2]);
40
+ const day = Number(match[3]);
41
+ const date = new Date(Date.UTC(year, month - 1, day));
42
+ if (date.getUTCFullYear() !== year || date.getUTCMonth() !== month - 1 || date.getUTCDate() !== day) {
43
+ throw new InvalidControlDeclarationDomainError("expiresOn must be a valid calendar date");
44
+ }
45
+ return value;
46
+ };
47
+
48
+ export interface AdoptionBaselineEntryProps {
49
+ readonly violationFingerprint: ViolationFingerprint;
50
+ readonly ruleId: WcrRuleId;
51
+ readonly constraintId: WorldNodeId | null;
52
+ }
53
+
54
+ export class AdoptionBaselineEntry {
55
+ readonly violationFingerprint: ViolationFingerprint;
56
+ readonly ruleId: WcrRuleId;
57
+ readonly constraintId: WorldNodeId | null;
58
+
59
+ private constructor(props: AdoptionBaselineEntryProps) {
60
+ this.violationFingerprint = props.violationFingerprint;
61
+ this.ruleId = props.ruleId;
62
+ this.constraintId = props.constraintId;
63
+ Object.freeze(this);
64
+ }
65
+
66
+ static create(props: AdoptionBaselineEntryProps): AdoptionBaselineEntry {
67
+ if (props.ruleId.toString() === "WCR-001") {
68
+ throw new InvalidControlDeclarationDomainError("WCR-001 is not adoptable");
69
+ }
70
+ if (props.constraintId !== null && props.constraintId.nodeType !== "constraint") {
71
+ throw new InvalidControlDeclarationDomainError("baseline constraintId must identify a constraint");
72
+ }
73
+ return new AdoptionBaselineEntry(props);
74
+ }
75
+
76
+ toCanonicalValue(): CanonicalJsonObject {
77
+ return {
78
+ constraintId: this.constraintId?.toString() ?? null,
79
+ ruleId: this.ruleId.toString(),
80
+ violationFingerprint: this.violationFingerprint.toString(),
81
+ };
82
+ }
83
+ }
84
+
85
+ export interface AdoptionBaselineProps {
86
+ readonly schemaVersion: string;
87
+ readonly rulesetVersion: string;
88
+ readonly sourceEvaluationId: EvaluationId;
89
+ readonly sourceCorpusRoot: Sha256Digest;
90
+ readonly sourceConstraintRoot: Sha256Digest;
91
+ readonly adoptedByWorkItemId: string;
92
+ readonly adoptionReason: string;
93
+ readonly entries: readonly AdoptionBaselineEntry[];
94
+ }
95
+
96
+ export class AdoptionBaseline {
97
+ readonly schemaVersion: string;
98
+ readonly rulesetVersion: string;
99
+ readonly sourceEvaluationId: EvaluationId;
100
+ readonly sourceCorpusRoot: Sha256Digest;
101
+ readonly sourceConstraintRoot: Sha256Digest;
102
+ readonly adoptedByWorkItemId: string;
103
+ readonly adoptionReason: string;
104
+ readonly entries: readonly AdoptionBaselineEntry[];
105
+
106
+ private constructor(props: AdoptionBaselineProps, entries: readonly AdoptionBaselineEntry[]) {
107
+ this.schemaVersion = props.schemaVersion;
108
+ this.rulesetVersion = props.rulesetVersion;
109
+ this.sourceEvaluationId = props.sourceEvaluationId;
110
+ this.sourceCorpusRoot = props.sourceCorpusRoot;
111
+ this.sourceConstraintRoot = props.sourceConstraintRoot;
112
+ this.adoptedByWorkItemId = props.adoptedByWorkItemId;
113
+ this.adoptionReason = props.adoptionReason;
114
+ this.entries = entries;
115
+ Object.freeze(this);
116
+ }
117
+
118
+ static create(props: AdoptionBaselineProps): AdoptionBaseline {
119
+ if (props.schemaVersion !== "phasegate-world-adoption-baseline/v1") {
120
+ throw new InvalidControlDeclarationDomainError("unsupported adoption baseline schemaVersion");
121
+ }
122
+ requireNonEmpty(props.rulesetVersion, "rulesetVersion");
123
+ requireWorkItemId(props.adoptedByWorkItemId, "adoptedByWorkItemId");
124
+ requireNonEmpty(props.adoptionReason, "adoptionReason");
125
+ const entries = [...props.entries].sort((left, right) =>
126
+ compareStrings(left.violationFingerprint.toString(), right.violationFingerprint.toString()),
127
+ );
128
+ const fingerprints = entries.map((entry) => entry.violationFingerprint.toString());
129
+ if (new Set(fingerprints).size !== fingerprints.length) {
130
+ throw new InvalidControlDeclarationDomainError("baseline entries contain duplicate violationFingerprint");
131
+ }
132
+ return new AdoptionBaseline(props, Object.freeze(entries));
133
+ }
134
+
135
+ toCanonicalValue(): CanonicalJsonObject {
136
+ return {
137
+ adoptedByWorkItemId: this.adoptedByWorkItemId,
138
+ adoptionReason: this.adoptionReason,
139
+ entries: this.entries.map((entry) => entry.toCanonicalValue()),
140
+ rulesetVersion: this.rulesetVersion,
141
+ schemaVersion: this.schemaVersion,
142
+ sourceConstraintRoot: this.sourceConstraintRoot.toString(),
143
+ sourceCorpusRoot: this.sourceCorpusRoot.toString(),
144
+ sourceEvaluationId: this.sourceEvaluationId.toString(),
145
+ };
146
+ }
147
+ }
148
+
149
+ export interface WorldWaiverProps {
150
+ readonly schemaVersion: string;
151
+ readonly waiverId: string;
152
+ readonly violationFingerprint: ViolationFingerprint;
153
+ readonly reason: string;
154
+ readonly expiresOn: string;
155
+ readonly workItemId: string;
156
+ readonly renewalOf: string | null;
157
+ }
158
+
159
+ export class WorldWaiver {
160
+ readonly schemaVersion: string;
161
+ readonly waiverId: string;
162
+ readonly violationFingerprint: ViolationFingerprint;
163
+ readonly reason: string;
164
+ readonly expiresOn: string;
165
+ readonly workItemId: string;
166
+ readonly renewalOf: string | null;
167
+
168
+ private constructor(props: WorldWaiverProps) {
169
+ this.schemaVersion = props.schemaVersion;
170
+ this.waiverId = props.waiverId;
171
+ this.violationFingerprint = props.violationFingerprint;
172
+ this.reason = props.reason;
173
+ this.expiresOn = props.expiresOn;
174
+ this.workItemId = props.workItemId;
175
+ this.renewalOf = props.renewalOf;
176
+ Object.freeze(this);
177
+ }
178
+
179
+ static create(props: WorldWaiverProps): WorldWaiver {
180
+ if (props.schemaVersion !== "phasegate-world-waivers/v1") {
181
+ throw new InvalidControlDeclarationDomainError("unsupported waiver schemaVersion");
182
+ }
183
+ if (!WAIVER_ID_PATTERN.test(props.waiverId)) {
184
+ throw new InvalidControlDeclarationDomainError("waiverId is invalid");
185
+ }
186
+ if (props.renewalOf !== null && !WAIVER_ID_PATTERN.test(props.renewalOf)) {
187
+ throw new InvalidControlDeclarationDomainError("renewalOf is invalid");
188
+ }
189
+ requireNonEmpty(props.reason, "reason");
190
+ requireWorkItemId(props.workItemId, "workItemId");
191
+ requireCalendarDate(props.expiresOn);
192
+ return new WorldWaiver(props);
193
+ }
194
+
195
+ toCanonicalValue(): CanonicalJsonObject {
196
+ return {
197
+ expiresOn: this.expiresOn,
198
+ reason: this.reason,
199
+ renewalOf: this.renewalOf,
200
+ schemaVersion: this.schemaVersion,
201
+ violationFingerprint: this.violationFingerprint.toString(),
202
+ waiverId: this.waiverId,
203
+ workItemId: this.workItemId,
204
+ };
205
+ }
206
+ }
207
+
208
+ export interface SemanticDebtDeclarationProps {
209
+ readonly schemaVersion: string;
210
+ readonly debtId: string;
211
+ readonly kind: "semantic";
212
+ readonly title: string;
213
+ readonly reason: string;
214
+ readonly ownerUnit: string;
215
+ readonly introducedByWorkItemId: string;
216
+ readonly references: readonly WorldNodeId[];
217
+ }
218
+
219
+ export class SemanticDebtDeclaration {
220
+ readonly schemaVersion: string;
221
+ readonly debtId: string;
222
+ readonly kind = "semantic" as const;
223
+ readonly title: string;
224
+ readonly reason: string;
225
+ readonly ownerUnit: string;
226
+ readonly introducedByWorkItemId: string;
227
+ readonly references: readonly WorldNodeId[];
228
+
229
+ private constructor(props: SemanticDebtDeclarationProps, references: readonly WorldNodeId[]) {
230
+ this.schemaVersion = props.schemaVersion;
231
+ this.debtId = props.debtId;
232
+ this.title = props.title;
233
+ this.reason = props.reason;
234
+ this.ownerUnit = props.ownerUnit;
235
+ this.introducedByWorkItemId = props.introducedByWorkItemId;
236
+ this.references = references;
237
+ Object.freeze(this);
238
+ }
239
+
240
+ static create(props: SemanticDebtDeclarationProps): SemanticDebtDeclaration {
241
+ if (props.schemaVersion !== "phasegate-world-debts/v1") {
242
+ throw new InvalidControlDeclarationDomainError("unsupported semantic debt schemaVersion");
243
+ }
244
+ if (!DEBT_ID_PATTERN.test(props.debtId)) {
245
+ throw new InvalidControlDeclarationDomainError("debtId is invalid");
246
+ }
247
+ if (props.kind !== "semantic") throw new InvalidControlDeclarationDomainError("kind must be semantic");
248
+ if (!UNIT_ID_PATTERN.test(props.ownerUnit)) {
249
+ throw new InvalidControlDeclarationDomainError("ownerUnit is invalid");
250
+ }
251
+ requireNonEmpty(props.title, "title");
252
+ requireNonEmpty(props.reason, "reason");
253
+ requireWorkItemId(props.introducedByWorkItemId, "introducedByWorkItemId");
254
+ const references = [...props.references].sort((left, right) => compareStrings(left.toString(), right.toString()));
255
+ const values = references.map((reference) => reference.toString());
256
+ if (new Set(values).size !== values.length) {
257
+ throw new InvalidControlDeclarationDomainError("references must be unique");
258
+ }
259
+ return new SemanticDebtDeclaration(props, Object.freeze(references));
260
+ }
261
+
262
+ toCanonicalValue(): CanonicalJsonObject {
263
+ return {
264
+ debtId: this.debtId,
265
+ introducedByWorkItemId: this.introducedByWorkItemId,
266
+ kind: this.kind,
267
+ ownerUnit: this.ownerUnit,
268
+ reason: this.reason,
269
+ references: this.references.map((reference) => reference.toString()),
270
+ schemaVersion: this.schemaVersion,
271
+ title: this.title,
272
+ };
273
+ }
274
+ }
@@ -0,0 +1,41 @@
1
+ // @unit world-model
2
+ // @layer domain
3
+ // @work-item-id WI-287
4
+ import type { CanonicalJsonObject } from "../services/canonical-json-serializer.js";
5
+ import type { DeclaredKey } from "../value-objects/declared-key.js";
6
+ import type { WorldNodeId } from "../value-objects/world-node-id.js";
7
+
8
+ export interface EdgeProps {
9
+ readonly edgeType: DeclaredKey;
10
+ readonly from: WorldNodeId;
11
+ readonly to: WorldNodeId;
12
+ readonly qualifier?: CanonicalJsonObject;
13
+ }
14
+
15
+ export class Edge {
16
+ readonly edgeType: DeclaredKey;
17
+ readonly from: WorldNodeId;
18
+ readonly to: WorldNodeId;
19
+ readonly qualifier: CanonicalJsonObject;
20
+
21
+ private constructor(props: EdgeProps) {
22
+ this.edgeType = props.edgeType;
23
+ this.from = props.from;
24
+ this.to = props.to;
25
+ this.qualifier = Object.freeze({ ...(props.qualifier ?? {}) });
26
+ Object.freeze(this);
27
+ }
28
+
29
+ static create(props: EdgeProps): Edge {
30
+ return new Edge(props);
31
+ }
32
+
33
+ toCanonicalValue(): CanonicalJsonObject {
34
+ return {
35
+ edgeType: this.edgeType.toString(),
36
+ from: this.from.toString(),
37
+ qualifier: this.qualifier,
38
+ to: this.to.toString(),
39
+ };
40
+ }
41
+ }
@@ -0,0 +1,60 @@
1
+ // @unit world-model
2
+ // @layer domain
3
+ // @work-item-id WI-287
4
+ import type { CanonicalJsonObject } from "../services/canonical-json-serializer.js";
5
+ import type { PathKey } from "../value-objects/path-key.js";
6
+ import type { WorldNodeId } from "../value-objects/world-node-id.js";
7
+
8
+ const DIAGNOSTIC_CODE_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
9
+
10
+ export interface ExtractionDiagnosticProps {
11
+ readonly code: string;
12
+ readonly nodeId?: WorldNodeId;
13
+ readonly path?: PathKey;
14
+ readonly line?: number;
15
+ readonly payload?: CanonicalJsonObject;
16
+ }
17
+
18
+ export class InvalidExtractionDiagnosticError extends Error {
19
+ constructor(message: string) {
20
+ super(`Invalid World extraction diagnostic: ${message}`);
21
+ this.name = "InvalidExtractionDiagnosticError";
22
+ }
23
+ }
24
+
25
+ export class ExtractionDiagnostic {
26
+ readonly code: string;
27
+ readonly nodeId?: WorldNodeId;
28
+ readonly path?: PathKey;
29
+ readonly line?: number;
30
+ readonly payload: CanonicalJsonObject;
31
+
32
+ private constructor(props: ExtractionDiagnosticProps) {
33
+ this.code = props.code;
34
+ this.nodeId = props.nodeId;
35
+ this.path = props.path;
36
+ this.line = props.line;
37
+ this.payload = Object.freeze({ ...(props.payload ?? {}) });
38
+ Object.freeze(this);
39
+ }
40
+
41
+ static create(props: ExtractionDiagnosticProps): ExtractionDiagnostic {
42
+ if (!DIAGNOSTIC_CODE_PATTERN.test(props.code)) {
43
+ throw new InvalidExtractionDiagnosticError(`invalid code "${props.code}"`);
44
+ }
45
+ if (props.line !== undefined && (!Number.isInteger(props.line) || props.line < 1)) {
46
+ throw new InvalidExtractionDiagnosticError(`invalid line "${props.line}"`);
47
+ }
48
+ return new ExtractionDiagnostic(props);
49
+ }
50
+
51
+ toCanonicalValue(): CanonicalJsonObject {
52
+ return {
53
+ code: this.code,
54
+ line: this.line ?? null,
55
+ nodeId: this.nodeId?.toString() ?? null,
56
+ pathKey: this.path?.toString() ?? null,
57
+ payload: this.payload,
58
+ };
59
+ }
60
+ }
@@ -0,0 +1,75 @@
1
+ // @unit world-model
2
+ // @layer domain
3
+ // @work-item-id WI-287
4
+
5
+ import type { EvaluationId } from "../value-objects/evaluation-id.js";
6
+ import type { Sha256Digest } from "../value-objects/sha256-digest.js";
7
+ import { WorldNodeId } from "../value-objects/world-node-id.js";
8
+ import type { Edge } from "./edge.js";
9
+ import type { ExtractionDiagnostic } from "./extraction-diagnostic.js";
10
+ import type { WorldNode } from "./world-node.js";
11
+
12
+ export interface SnapshotProps {
13
+ readonly schemaVersion: string;
14
+ readonly extractorVersion: string;
15
+ readonly corpusConfigDigest: Sha256Digest;
16
+ readonly nodes: readonly WorldNode[];
17
+ readonly edges: readonly Edge[];
18
+ readonly extractionDiagnostics: readonly ExtractionDiagnostic[];
19
+ readonly corpusRoot: Sha256Digest;
20
+ readonly canonicalBytes: Uint8Array;
21
+ readonly constraintRoot?: Sha256Digest;
22
+ readonly evaluationId?: EvaluationId;
23
+ }
24
+
25
+ export class Snapshot {
26
+ readonly id: WorldNodeId;
27
+ readonly schemaVersion: string;
28
+ readonly extractorVersion: string;
29
+ readonly corpusConfigDigest: Sha256Digest;
30
+ readonly nodes: readonly WorldNode[];
31
+ readonly edges: readonly Edge[];
32
+ readonly extractionDiagnostics: readonly ExtractionDiagnostic[];
33
+ readonly corpusRoot: Sha256Digest;
34
+ readonly constraintRoot?: Sha256Digest;
35
+ readonly evaluationId?: EvaluationId;
36
+ private readonly canonicalBytesValue: Uint8Array;
37
+
38
+ private constructor(props: SnapshotProps) {
39
+ this.id = WorldNodeId.snapshot(props.corpusRoot);
40
+ this.schemaVersion = props.schemaVersion;
41
+ this.extractorVersion = props.extractorVersion;
42
+ this.corpusConfigDigest = props.corpusConfigDigest;
43
+ this.nodes = Object.freeze([...props.nodes]);
44
+ this.edges = Object.freeze([...props.edges]);
45
+ this.extractionDiagnostics = Object.freeze([...props.extractionDiagnostics]);
46
+ this.corpusRoot = props.corpusRoot;
47
+ this.canonicalBytesValue = Uint8Array.from(props.canonicalBytes);
48
+ this.constraintRoot = props.constraintRoot;
49
+ this.evaluationId = props.evaluationId;
50
+ Object.freeze(this);
51
+ }
52
+
53
+ static create(props: SnapshotProps): Snapshot {
54
+ return new Snapshot(props);
55
+ }
56
+
57
+ get canonicalBytes(): Uint8Array {
58
+ return Uint8Array.from(this.canonicalBytesValue);
59
+ }
60
+
61
+ withEvaluationRoots(constraintRoot: Sha256Digest, evaluationId: EvaluationId): Snapshot {
62
+ return new Snapshot({
63
+ schemaVersion: this.schemaVersion,
64
+ extractorVersion: this.extractorVersion,
65
+ corpusConfigDigest: this.corpusConfigDigest,
66
+ nodes: this.nodes,
67
+ edges: this.edges,
68
+ extractionDiagnostics: this.extractionDiagnostics,
69
+ corpusRoot: this.corpusRoot,
70
+ canonicalBytes: this.canonicalBytes,
71
+ constraintRoot,
72
+ evaluationId,
73
+ });
74
+ }
75
+ }