phasegate 0.254.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 (65) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.ja.md +6 -3
  3. package/README.md +6 -3
  4. package/docs/contracts/attestation-v2.schema.json +110 -0
  5. package/docs/guide/cli-reference.md +12 -1
  6. package/docs/guide/configuration.md +56 -0
  7. package/docs/templates/ci/aidlc-gate.yml +42 -2
  8. package/package.json +1 -1
  9. package/scripts/harness/agent-integration/application/dto/open-world-obligations-context-dto.ts +30 -0
  10. package/scripts/harness/agent-integration/application/ports/world-obligations-query-port.ts +30 -0
  11. package/scripts/harness/agent-integration/application/usecases/get-open-world-obligations-context-usecase.ts +66 -0
  12. package/scripts/harness/agent-integration/infrastructure/adapters/world-model-open-obligations-query-adapter.ts +58 -0
  13. package/scripts/harness/agent-integration/presentation/session-start-hook.ts +37 -1
  14. package/scripts/harness/agent-integration/presentation/world-obligations-session-context.ts +60 -0
  15. package/scripts/harness/attestation/application/dto/attestation-document.ts +17 -4
  16. package/scripts/harness/attestation/application/mappers/attestation-record-mapper.ts +55 -4
  17. package/scripts/harness/attestation/application/ports/world-snapshot-root-provider.ts +10 -0
  18. package/scripts/harness/attestation/application/usecases/produce-attestation-usecase.ts +33 -7
  19. package/scripts/harness/attestation/composition-root.ts +5 -0
  20. package/scripts/harness/attestation/domain/entities/attestation-record.ts +37 -2
  21. package/scripts/harness/attestation/index.ts +7 -1
  22. package/scripts/harness/attestation/presentation/handlers/attest-handler.ts +5 -2
  23. package/scripts/harness/config-foundation/application/mappers/validator-system-config-mapper.ts +35 -8
  24. package/scripts/harness/config-foundation/application/mappers/world-model-config-mapper.ts +15 -0
  25. package/scripts/harness/config-foundation/domain/harness-config.ts +67 -87
  26. package/scripts/harness/config-foundation/domain/services/preset-resolution-service.ts +77 -88
  27. package/scripts/harness/config-foundation/domain/value-objects/world-config.ts +198 -0
  28. package/scripts/harness/config-foundation/index.ts +14 -15
  29. package/scripts/harness/config-foundation/infrastructure/presets/minimal.json +24 -0
  30. package/scripts/harness/config-foundation/infrastructure/presets/standard.json +24 -0
  31. package/scripts/harness/config-foundation/infrastructure/presets/strict.json +24 -0
  32. package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v2.schema.json +83 -0
  33. package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v3.schema.json +83 -0
  34. package/scripts/harness/harness-api/domain/value-objects/ci-check-result.ts +18 -4
  35. package/scripts/harness/integrations/pre-commit.ts +169 -27
  36. package/scripts/harness/main.ts +174 -130
  37. package/scripts/harness/traceability-model/application/dto/changed-design-fragment-dto.ts +31 -0
  38. package/scripts/harness/traceability-model/application/facades/design-change-read-facade.ts +14 -0
  39. package/scripts/harness/traceability-model/application/ports/staged-design-change-source-port.ts +9 -0
  40. package/scripts/harness/traceability-model/composition-root.ts +5 -0
  41. package/scripts/harness/traceability-model/index.ts +9 -0
  42. package/scripts/harness/traceability-model/infrastructure/adapters/git-staged-design-change-adapter.ts +155 -0
  43. package/scripts/harness/validator-system/application/use-cases/run-l2-validators-usecase.ts +31 -0
  44. package/scripts/harness/validator-system/application/use-cases/run-l3-validators-usecase.ts +34 -0
  45. package/scripts/harness/validator-system/composition-root.ts +21 -6
  46. package/scripts/harness/validator-system/domain/ports/world-constraint-admission-policy-port.ts +28 -0
  47. package/scripts/harness/validator-system/domain/ports/world-constraint-rederivation-policy-port.ts +11 -0
  48. package/scripts/harness/validator-system/domain/services/design-change-declaration-policy.ts +75 -0
  49. package/scripts/harness/validator-system/domain/services/world-constraint-admission-service.ts +78 -0
  50. package/scripts/harness/validator-system/domain/services/world-constraint-rederivation-service.ts +76 -0
  51. package/scripts/harness/validator-system/domain/value-objects/validator-id.ts +4 -0
  52. package/scripts/harness/validator-system/infrastructure/adapters/harness-config-validator-config-adapter.ts +38 -16
  53. package/scripts/harness/validator-system/infrastructure/adapters/world-model-constraint-admission-adapter.ts +56 -0
  54. package/scripts/harness/validator-system/infrastructure/adapters/world-model-constraint-rederivation-adapter.ts +56 -0
  55. package/scripts/harness/world-model/application/dto/pinned-design-endpoint-dto.ts +19 -0
  56. package/scripts/harness/world-model/application/dto/world-resolved-config-input.ts +92 -27
  57. package/scripts/harness/world-model/application/dto/world-snapshot-root-dto.ts +8 -0
  58. package/scripts/harness/world-model/application/facades/pinned-design-endpoint-facade.ts +63 -0
  59. package/scripts/harness/world-model/application/facades/world-snapshot-root-facade.ts +19 -0
  60. package/scripts/harness/world-model/application/usecases/derive-world-obligations-use-case.ts +3 -1
  61. package/scripts/harness/world-model/composition-root.ts +62 -33
  62. package/scripts/harness/world-model/index.ts +11 -0
  63. package/scripts/harness/world-model/infrastructure/adapters/attestation-fact-extractor.ts +48 -13
  64. package/scripts/harness/world-model/infrastructure/adapters/file-system-world-control-repository-adapters.ts +6 -4
  65. package/scripts/harness/world-model/presentation/cli/world-derive-command-handler.ts +6 -2
@@ -0,0 +1,198 @@
1
+ // @unit config-foundation
2
+ // @layer domain
3
+ // @work-item-id WI-300
4
+
5
+ import { ConfigValidationError } from "../errors/config-validation-error.js";
6
+
7
+ export interface WorldConfigDocument {
8
+ readonly enabled: boolean;
9
+ readonly corpus: {
10
+ readonly productRoots: readonly string[];
11
+ readonly inceptionRoots: readonly string[];
12
+ readonly adrRoots: readonly string[];
13
+ readonly sourceRoots: readonly string[];
14
+ readonly include: readonly string[];
15
+ readonly exclude: readonly string[];
16
+ };
17
+ readonly inputs: {
18
+ readonly matrixPath: string;
19
+ readonly attestationPath: string;
20
+ readonly integrityManifestPath: string;
21
+ };
22
+ readonly declarations: {
23
+ readonly constraintsPath: string;
24
+ readonly baselinePath: string;
25
+ readonly waiversPath: string;
26
+ readonly debtsPath: string;
27
+ };
28
+ readonly output: {
29
+ readonly obligationReportPath: string;
30
+ };
31
+ readonly sessionStart: {
32
+ readonly enabled: boolean;
33
+ readonly maxItems: number;
34
+ readonly maxChars: number;
35
+ };
36
+ }
37
+
38
+ export type WorldConfigSourceDocument = {
39
+ readonly [K in keyof WorldConfigDocument]?: WorldConfigDocument[K] extends readonly string[]
40
+ ? readonly string[]
41
+ : WorldConfigDocument[K] extends object
42
+ ? Partial<WorldConfigDocument[K]>
43
+ : WorldConfigDocument[K];
44
+ };
45
+
46
+ export const WORLD_CONFIG_DEFAULTS: WorldConfigDocument = Object.freeze({
47
+ enabled: false,
48
+ corpus: Object.freeze({
49
+ productRoots: Object.freeze(["docs/product"]),
50
+ inceptionRoots: Object.freeze(["docs/inception"]),
51
+ adrRoots: Object.freeze(["docs/ADR"]),
52
+ sourceRoots: Object.freeze(["scripts/harness"]),
53
+ include: Object.freeze(["**/*"]),
54
+ exclude: Object.freeze([]),
55
+ }),
56
+ inputs: Object.freeze({
57
+ matrixPath: ".harness/requirement-test-matrix.json",
58
+ attestationPath: ".harness/attestation.json",
59
+ integrityManifestPath: "phasegate.integrity.json",
60
+ }),
61
+ declarations: Object.freeze({
62
+ constraintsPath: "phasegate.world-constraints.json",
63
+ baselinePath: "phasegate.world-baseline.json",
64
+ waiversPath: "phasegate.world-waivers.json",
65
+ debtsPath: "phasegate.world-debts.json",
66
+ }),
67
+ output: Object.freeze({
68
+ obligationReportPath: ".harness/world-obligations.json",
69
+ }),
70
+ sessionStart: Object.freeze({ enabled: true, maxItems: 5, maxChars: 2000 }),
71
+ });
72
+
73
+ const cloneDocument = (document: WorldConfigDocument): WorldConfigDocument =>
74
+ Object.freeze({
75
+ enabled: document.enabled,
76
+ corpus: Object.freeze({
77
+ productRoots: Object.freeze([...document.corpus.productRoots]),
78
+ inceptionRoots: Object.freeze([...document.corpus.inceptionRoots]),
79
+ adrRoots: Object.freeze([...document.corpus.adrRoots]),
80
+ sourceRoots: Object.freeze([...document.corpus.sourceRoots]),
81
+ include: Object.freeze([...document.corpus.include]),
82
+ exclude: Object.freeze([...document.corpus.exclude]),
83
+ }),
84
+ inputs: Object.freeze({ ...document.inputs }),
85
+ declarations: Object.freeze({ ...document.declarations }),
86
+ output: Object.freeze({ ...document.output }),
87
+ sessionStart: Object.freeze({ ...document.sessionStart }),
88
+ });
89
+
90
+ const assertProjectRelativePosixPath = (value: string): void => {
91
+ const segments = value.split("/");
92
+ if (
93
+ value.length === 0 ||
94
+ value.startsWith("/") ||
95
+ value.includes("\\") ||
96
+ segments.some((segment) => segment === "" || segment === "." || segment === "..")
97
+ ) {
98
+ throw new ConfigValidationError(`world path must be a project-relative POSIX path: ${value}`);
99
+ }
100
+ };
101
+
102
+ const assertUniqueStrings = (name: string, values: readonly string[], allowEmpty: boolean): void => {
103
+ if (!allowEmpty && values.length === 0) {
104
+ throw new ConfigValidationError(`${name} must not be empty`);
105
+ }
106
+ if (values.some((value) => value.length === 0) || new Set(values).size !== values.length) {
107
+ throw new ConfigValidationError(`${name} must contain unique non-empty values`);
108
+ }
109
+ };
110
+
111
+ interface CorpusRootEntry {
112
+ readonly role: string;
113
+ readonly path: string;
114
+ }
115
+
116
+ const rootsOverlap = (left: string, right: string): boolean =>
117
+ left === right || left.startsWith(`${right}/`) || right.startsWith(`${left}/`);
118
+
119
+ const assertCorpusRoots = (document: WorldConfigDocument): void => {
120
+ const entries: readonly CorpusRootEntry[] = [
121
+ ...document.corpus.productRoots.map((path) => ({ role: "product", path })),
122
+ ...document.corpus.inceptionRoots.map((path) => ({ role: "inception", path })),
123
+ ...document.corpus.adrRoots.map((path) => ({ role: "adr", path })),
124
+ ...document.corpus.sourceRoots.map((path) => ({ role: "source", path })),
125
+ ];
126
+
127
+ for (let leftIndex = 0; leftIndex < entries.length; leftIndex += 1) {
128
+ for (let rightIndex = leftIndex + 1; rightIndex < entries.length; rightIndex += 1) {
129
+ const left = entries[leftIndex];
130
+ const right = entries[rightIndex];
131
+ if (left.path.toLowerCase() === right.path.toLowerCase() && left.path !== right.path) {
132
+ throw new ConfigValidationError(`world path case-fold collision: ${left.path} / ${right.path}`);
133
+ }
134
+ if (left.role !== right.role && rootsOverlap(left.path, right.path)) {
135
+ throw new ConfigValidationError(`world corpus root role overlap: ${left.path} / ${right.path}`);
136
+ }
137
+ }
138
+ }
139
+ };
140
+
141
+ const allPaths = (document: WorldConfigDocument): readonly string[] => [
142
+ ...document.corpus.productRoots,
143
+ ...document.corpus.inceptionRoots,
144
+ ...document.corpus.adrRoots,
145
+ ...document.corpus.sourceRoots,
146
+ document.inputs.matrixPath,
147
+ document.inputs.attestationPath,
148
+ document.inputs.integrityManifestPath,
149
+ document.declarations.constraintsPath,
150
+ document.declarations.baselinePath,
151
+ document.declarations.waiversPath,
152
+ document.declarations.debtsPath,
153
+ document.output.obligationReportPath,
154
+ ];
155
+
156
+ const assertNoCaseFoldPathCollisions = (paths: readonly string[]): void => {
157
+ const byFoldedPath = new Map<string, string>();
158
+ for (const path of paths) {
159
+ const folded = path.toLowerCase();
160
+ const existing = byFoldedPath.get(folded);
161
+ if (existing !== undefined && existing !== path) {
162
+ throw new ConfigValidationError(`world path case-fold collision: ${existing} / ${path}`);
163
+ }
164
+ byFoldedPath.set(folded, path);
165
+ }
166
+ };
167
+
168
+ export class WorldConfig {
169
+ private constructor(private readonly document: WorldConfigDocument) {}
170
+
171
+ static create(document: WorldConfigDocument): WorldConfig {
172
+ assertUniqueStrings("world.corpus.productRoots", document.corpus.productRoots, false);
173
+ assertUniqueStrings("world.corpus.inceptionRoots", document.corpus.inceptionRoots, false);
174
+ assertUniqueStrings("world.corpus.adrRoots", document.corpus.adrRoots, false);
175
+ assertUniqueStrings("world.corpus.sourceRoots", document.corpus.sourceRoots, false);
176
+ assertUniqueStrings("world.corpus.include", document.corpus.include, false);
177
+ assertUniqueStrings("world.corpus.exclude", document.corpus.exclude, true);
178
+ const paths = allPaths(document);
179
+ for (const path of paths) assertProjectRelativePosixPath(path);
180
+ assertNoCaseFoldPathCollisions(paths);
181
+ assertCorpusRoots(document);
182
+ if (
183
+ !Number.isInteger(document.sessionStart.maxItems) ||
184
+ document.sessionStart.maxItems < 1 ||
185
+ document.sessionStart.maxItems > 20 ||
186
+ !Number.isInteger(document.sessionStart.maxChars) ||
187
+ document.sessionStart.maxChars < 1 ||
188
+ document.sessionStart.maxChars > 8000
189
+ ) {
190
+ throw new ConfigValidationError("world.sessionStart must use maxItems 1..20 and maxChars 1..8000");
191
+ }
192
+ return new WorldConfig(cloneDocument(document));
193
+ }
194
+
195
+ toDocument(): WorldConfigDocument {
196
+ return cloneDocument(this.document);
197
+ }
198
+ }
@@ -1,18 +1,17 @@
1
1
  // @unit config-foundation
2
2
  // @layer application
3
+ // @work-item-id WI-300
3
4
 
4
- export { createConfigFoundationModule } from './composition-root.js';
5
-
6
- export { LoadResolvedConfigUseCase } from './application/usecases/load-resolved-config-use-case.js';
7
- export { toValidatorSystemConfig } from './application/mappers/validator-system-config-mapper.js';
8
-
9
- export type { ResolvedConfigOutput } from './application/dto/resolved-config-output.js';
10
- export type { FeatureToggleResult } from './application/dto/feature-toggle-result.js';
11
- export type { AvailableFeatureItem } from './application/dto/available-feature-item.js';
12
-
13
- export { EnableFeatureCommandHandler } from './presentation/cli/enable-feature-command-handler.js';
14
- export { DisableFeatureCommandHandler } from './presentation/cli/disable-feature-command-handler.js';
15
-
16
- export type { ConfigRepositoryPort } from './domain/ports/config-repository-port.js';
17
- export type { ConfigSchemaValidatorPort } from './domain/ports/config-schema-validator-port.js';
18
- export type { FeatureRegistryPort } from './domain/ports/feature-registry-port.js';
5
+ export type { AvailableFeatureItem } from "./application/dto/available-feature-item.js";
6
+ export type { FeatureToggleResult } from "./application/dto/feature-toggle-result.js";
7
+ export type { ResolvedConfigOutput } from "./application/dto/resolved-config-output.js";
8
+ export { toValidatorSystemConfig } from "./application/mappers/validator-system-config-mapper.js";
9
+ export { toWorldModelConfig } from "./application/mappers/world-model-config-mapper.js";
10
+ export { LoadResolvedConfigUseCase } from "./application/usecases/load-resolved-config-use-case.js";
11
+ export { createConfigFoundationModule } from "./composition-root.js";
12
+ export type { ConfigRepositoryPort } from "./domain/ports/config-repository-port.js";
13
+ export type { ConfigSchemaValidatorPort } from "./domain/ports/config-schema-validator-port.js";
14
+ export type { FeatureRegistryPort } from "./domain/ports/feature-registry-port.js";
15
+ export type { WorldConfigDocument } from "./domain/value-objects/world-config.js";
16
+ export { DisableFeatureCommandHandler } from "./presentation/cli/disable-feature-command-handler.js";
17
+ export { EnableFeatureCommandHandler } from "./presentation/cli/enable-feature-command-handler.js";
@@ -55,5 +55,29 @@
55
55
  },
56
56
  "preCommit": {
57
57
  "implementationExtensions": [".ts"]
58
+ },
59
+ "world": {
60
+ "enabled": false,
61
+ "corpus": {
62
+ "productRoots": ["docs/product"],
63
+ "inceptionRoots": ["docs/inception"],
64
+ "adrRoots": ["docs/ADR"],
65
+ "sourceRoots": ["scripts/harness"],
66
+ "include": ["**/*"],
67
+ "exclude": []
68
+ },
69
+ "inputs": {
70
+ "matrixPath": ".harness/requirement-test-matrix.json",
71
+ "attestationPath": ".harness/attestation.json",
72
+ "integrityManifestPath": "phasegate.integrity.json"
73
+ },
74
+ "declarations": {
75
+ "constraintsPath": "phasegate.world-constraints.json",
76
+ "baselinePath": "phasegate.world-baseline.json",
77
+ "waiversPath": "phasegate.world-waivers.json",
78
+ "debtsPath": "phasegate.world-debts.json"
79
+ },
80
+ "output": { "obligationReportPath": ".harness/world-obligations.json" },
81
+ "sessionStart": { "enabled": true, "maxItems": 5, "maxChars": 2000 }
58
82
  }
59
83
  }
@@ -55,5 +55,29 @@
55
55
  },
56
56
  "preCommit": {
57
57
  "implementationExtensions": [".ts"]
58
+ },
59
+ "world": {
60
+ "enabled": false,
61
+ "corpus": {
62
+ "productRoots": ["docs/product"],
63
+ "inceptionRoots": ["docs/inception"],
64
+ "adrRoots": ["docs/ADR"],
65
+ "sourceRoots": ["scripts/harness"],
66
+ "include": ["**/*"],
67
+ "exclude": []
68
+ },
69
+ "inputs": {
70
+ "matrixPath": ".harness/requirement-test-matrix.json",
71
+ "attestationPath": ".harness/attestation.json",
72
+ "integrityManifestPath": "phasegate.integrity.json"
73
+ },
74
+ "declarations": {
75
+ "constraintsPath": "phasegate.world-constraints.json",
76
+ "baselinePath": "phasegate.world-baseline.json",
77
+ "waiversPath": "phasegate.world-waivers.json",
78
+ "debtsPath": "phasegate.world-debts.json"
79
+ },
80
+ "output": { "obligationReportPath": ".harness/world-obligations.json" },
81
+ "sessionStart": { "enabled": true, "maxItems": 5, "maxChars": 2000 }
58
82
  }
59
83
  }
@@ -55,5 +55,29 @@
55
55
  },
56
56
  "preCommit": {
57
57
  "implementationExtensions": [".ts"]
58
+ },
59
+ "world": {
60
+ "enabled": false,
61
+ "corpus": {
62
+ "productRoots": ["docs/product"],
63
+ "inceptionRoots": ["docs/inception"],
64
+ "adrRoots": ["docs/ADR"],
65
+ "sourceRoots": ["scripts/harness"],
66
+ "include": ["**/*"],
67
+ "exclude": []
68
+ },
69
+ "inputs": {
70
+ "matrixPath": ".harness/requirement-test-matrix.json",
71
+ "attestationPath": ".harness/attestation.json",
72
+ "integrityManifestPath": "phasegate.integrity.json"
73
+ },
74
+ "declarations": {
75
+ "constraintsPath": "phasegate.world-constraints.json",
76
+ "baselinePath": "phasegate.world-baseline.json",
77
+ "waiversPath": "phasegate.world-waivers.json",
78
+ "debtsPath": "phasegate.world-debts.json"
79
+ },
80
+ "output": { "obligationReportPath": ".harness/world-obligations.json" },
81
+ "sessionStart": { "enabled": true, "maxItems": 5, "maxChars": 2000 }
58
82
  }
59
83
  }
@@ -477,6 +477,89 @@
477
477
  }
478
478
  }
479
479
  },
480
+ "world": {
481
+ "type": "object",
482
+ "additionalProperties": false,
483
+ "properties": {
484
+ "enabled": { "type": "boolean" },
485
+ "corpus": {
486
+ "type": "object",
487
+ "additionalProperties": false,
488
+ "properties": {
489
+ "productRoots": {
490
+ "type": "array",
491
+ "minItems": 1,
492
+ "uniqueItems": true,
493
+ "items": { "type": "string", "pattern": "^(?!/)(?!.*\\\\)(?!\\.\\.?(?:/|$))(?!.*\\/\\.\\.?(?:/|$)).+$" }
494
+ },
495
+ "inceptionRoots": {
496
+ "type": "array",
497
+ "minItems": 1,
498
+ "uniqueItems": true,
499
+ "items": { "type": "string", "pattern": "^(?!/)(?!.*\\\\)(?!\\.\\.?(?:/|$))(?!.*\\/\\.\\.?(?:/|$)).+$" }
500
+ },
501
+ "adrRoots": {
502
+ "type": "array",
503
+ "minItems": 1,
504
+ "uniqueItems": true,
505
+ "items": { "type": "string", "pattern": "^(?!/)(?!.*\\\\)(?!\\.\\.?(?:/|$))(?!.*\\/\\.\\.?(?:/|$)).+$" }
506
+ },
507
+ "sourceRoots": {
508
+ "type": "array",
509
+ "minItems": 1,
510
+ "uniqueItems": true,
511
+ "items": { "type": "string", "pattern": "^(?!/)(?!.*\\\\)(?!\\.\\.?(?:/|$))(?!.*\\/\\.\\.?(?:/|$)).+$" }
512
+ },
513
+ "include": {
514
+ "type": "array",
515
+ "minItems": 1,
516
+ "uniqueItems": true,
517
+ "items": { "type": "string", "minLength": 1 }
518
+ },
519
+ "exclude": {
520
+ "type": "array",
521
+ "uniqueItems": true,
522
+ "items": { "type": "string", "minLength": 1 }
523
+ }
524
+ }
525
+ },
526
+ "inputs": {
527
+ "type": "object",
528
+ "additionalProperties": false,
529
+ "properties": {
530
+ "matrixPath": { "type": "string", "pattern": "^(?!/)(?!.*\\\\)(?!\\.\\.?(?:/|$))(?!.*\\/\\.\\.?(?:/|$)).+$" },
531
+ "attestationPath": { "type": "string", "pattern": "^(?!/)(?!.*\\\\)(?!\\.\\.?(?:/|$))(?!.*\\/\\.\\.?(?:/|$)).+$" },
532
+ "integrityManifestPath": { "type": "string", "pattern": "^(?!/)(?!.*\\\\)(?!\\.\\.?(?:/|$))(?!.*\\/\\.\\.?(?:/|$)).+$" }
533
+ }
534
+ },
535
+ "declarations": {
536
+ "type": "object",
537
+ "additionalProperties": false,
538
+ "properties": {
539
+ "constraintsPath": { "type": "string", "pattern": "^(?!/)(?!.*\\\\)(?!\\.\\.?(?:/|$))(?!.*\\/\\.\\.?(?:/|$)).+$" },
540
+ "baselinePath": { "type": "string", "pattern": "^(?!/)(?!.*\\\\)(?!\\.\\.?(?:/|$))(?!.*\\/\\.\\.?(?:/|$)).+$" },
541
+ "waiversPath": { "type": "string", "pattern": "^(?!/)(?!.*\\\\)(?!\\.\\.?(?:/|$))(?!.*\\/\\.\\.?(?:/|$)).+$" },
542
+ "debtsPath": { "type": "string", "pattern": "^(?!/)(?!.*\\\\)(?!\\.\\.?(?:/|$))(?!.*\\/\\.\\.?(?:/|$)).+$" }
543
+ }
544
+ },
545
+ "output": {
546
+ "type": "object",
547
+ "additionalProperties": false,
548
+ "properties": {
549
+ "obligationReportPath": { "type": "string", "pattern": "^(?!/)(?!.*\\\\)(?!\\.\\.?(?:/|$))(?!.*\\/\\.\\.?(?:/|$)).+$" }
550
+ }
551
+ },
552
+ "sessionStart": {
553
+ "type": "object",
554
+ "additionalProperties": false,
555
+ "properties": {
556
+ "enabled": { "type": "boolean" },
557
+ "maxItems": { "type": "integer", "minimum": 1, "maximum": 20 },
558
+ "maxChars": { "type": "integer", "minimum": 1, "maximum": 8000 }
559
+ }
560
+ }
561
+ }
562
+ },
480
563
  "ci": {
481
564
  "type": "object",
482
565
  "additionalProperties": false,
@@ -495,6 +495,89 @@
495
495
  }
496
496
  }
497
497
  },
498
+ "world": {
499
+ "type": "object",
500
+ "additionalProperties": false,
501
+ "properties": {
502
+ "enabled": { "type": "boolean" },
503
+ "corpus": {
504
+ "type": "object",
505
+ "additionalProperties": false,
506
+ "properties": {
507
+ "productRoots": {
508
+ "type": "array",
509
+ "minItems": 1,
510
+ "uniqueItems": true,
511
+ "items": { "type": "string", "pattern": "^(?!/)(?!.*\\\\)(?!\\.\\.?(?:/|$))(?!.*\\/\\.\\.?(?:/|$)).+$" }
512
+ },
513
+ "inceptionRoots": {
514
+ "type": "array",
515
+ "minItems": 1,
516
+ "uniqueItems": true,
517
+ "items": { "type": "string", "pattern": "^(?!/)(?!.*\\\\)(?!\\.\\.?(?:/|$))(?!.*\\/\\.\\.?(?:/|$)).+$" }
518
+ },
519
+ "adrRoots": {
520
+ "type": "array",
521
+ "minItems": 1,
522
+ "uniqueItems": true,
523
+ "items": { "type": "string", "pattern": "^(?!/)(?!.*\\\\)(?!\\.\\.?(?:/|$))(?!.*\\/\\.\\.?(?:/|$)).+$" }
524
+ },
525
+ "sourceRoots": {
526
+ "type": "array",
527
+ "minItems": 1,
528
+ "uniqueItems": true,
529
+ "items": { "type": "string", "pattern": "^(?!/)(?!.*\\\\)(?!\\.\\.?(?:/|$))(?!.*\\/\\.\\.?(?:/|$)).+$" }
530
+ },
531
+ "include": {
532
+ "type": "array",
533
+ "minItems": 1,
534
+ "uniqueItems": true,
535
+ "items": { "type": "string", "minLength": 1 }
536
+ },
537
+ "exclude": {
538
+ "type": "array",
539
+ "uniqueItems": true,
540
+ "items": { "type": "string", "minLength": 1 }
541
+ }
542
+ }
543
+ },
544
+ "inputs": {
545
+ "type": "object",
546
+ "additionalProperties": false,
547
+ "properties": {
548
+ "matrixPath": { "type": "string", "pattern": "^(?!/)(?!.*\\\\)(?!\\.\\.?(?:/|$))(?!.*\\/\\.\\.?(?:/|$)).+$" },
549
+ "attestationPath": { "type": "string", "pattern": "^(?!/)(?!.*\\\\)(?!\\.\\.?(?:/|$))(?!.*\\/\\.\\.?(?:/|$)).+$" },
550
+ "integrityManifestPath": { "type": "string", "pattern": "^(?!/)(?!.*\\\\)(?!\\.\\.?(?:/|$))(?!.*\\/\\.\\.?(?:/|$)).+$" }
551
+ }
552
+ },
553
+ "declarations": {
554
+ "type": "object",
555
+ "additionalProperties": false,
556
+ "properties": {
557
+ "constraintsPath": { "type": "string", "pattern": "^(?!/)(?!.*\\\\)(?!\\.\\.?(?:/|$))(?!.*\\/\\.\\.?(?:/|$)).+$" },
558
+ "baselinePath": { "type": "string", "pattern": "^(?!/)(?!.*\\\\)(?!\\.\\.?(?:/|$))(?!.*\\/\\.\\.?(?:/|$)).+$" },
559
+ "waiversPath": { "type": "string", "pattern": "^(?!/)(?!.*\\\\)(?!\\.\\.?(?:/|$))(?!.*\\/\\.\\.?(?:/|$)).+$" },
560
+ "debtsPath": { "type": "string", "pattern": "^(?!/)(?!.*\\\\)(?!\\.\\.?(?:/|$))(?!.*\\/\\.\\.?(?:/|$)).+$" }
561
+ }
562
+ },
563
+ "output": {
564
+ "type": "object",
565
+ "additionalProperties": false,
566
+ "properties": {
567
+ "obligationReportPath": { "type": "string", "pattern": "^(?!/)(?!.*\\\\)(?!\\.\\.?(?:/|$))(?!.*\\/\\.\\.?(?:/|$)).+$" }
568
+ }
569
+ },
570
+ "sessionStart": {
571
+ "type": "object",
572
+ "additionalProperties": false,
573
+ "properties": {
574
+ "enabled": { "type": "boolean" },
575
+ "maxItems": { "type": "integer", "minimum": 1, "maximum": 20 },
576
+ "maxChars": { "type": "integer", "minimum": 1, "maximum": 8000 }
577
+ }
578
+ }
579
+ }
580
+ },
498
581
  "ci": {
499
582
  "type": "object",
500
583
  "additionalProperties": false,
@@ -1,5 +1,6 @@
1
1
  // @layer domain
2
2
  // @unit harness-api
3
+ // @work-item-id WI-307
3
4
  // ci-check-result.ts — CiCheckResult Value Object
4
5
 
5
6
  import type { HarnessError } from "./harness-api-response.js";
@@ -40,12 +41,25 @@ function isEffectivelyPassed(item: ValidatorCheckItem, failOnWarning: boolean):
40
41
  return !hasFail;
41
42
  }
42
43
 
44
+ /**
45
+ * WI-307: public ci-check projection の `passed` を aggregate policy と一致させる。
46
+ * skipped は独立した green state のため raw passed を維持し、warning-only failure だけを
47
+ * failOnWarning=false のとき passed=true に射影する。diagnostic は lossless に保持する。
48
+ */
49
+ function toPublicValidatorResult(item: ValidatorCheckItem, failOnWarning: boolean): ValidatorCheckItem {
50
+ return Object.freeze({
51
+ ...item,
52
+ passed: item.skipped ? item.passed : isEffectivelyPassed(item, failOnWarning),
53
+ errors: item.errors === undefined ? undefined : Object.freeze([...item.errors]),
54
+ });
55
+ }
56
+
43
57
  export class CiCheckResult {
44
58
  readonly validatorResults: readonly ValidatorCheckItem[];
45
59
  readonly allPassed: boolean;
46
60
 
47
- private constructor(validatorResults: readonly ValidatorCheckItem[], allPassed: boolean) {
48
- this.validatorResults = Object.freeze([...validatorResults]);
61
+ private constructor(validatorResults: readonly ValidatorCheckItem[], allPassed: boolean, failOnWarning: boolean) {
62
+ this.validatorResults = Object.freeze(validatorResults.map((item) => toPublicValidatorResult(item, failOnWarning)));
49
63
  this.allPassed = allPassed;
50
64
  Object.freeze(this);
51
65
  }
@@ -63,7 +77,7 @@ export class CiCheckResult {
63
77
  `HarnessApiDomainError: allPassed=${props.allPassed} does not match validatorResults state (computed: ${computedAllPassed})`,
64
78
  );
65
79
  }
66
- return new CiCheckResult(props.validatorResults, props.allPassed);
80
+ return new CiCheckResult(props.validatorResults, props.allPassed, failOnWarning);
67
81
  }
68
82
 
69
83
  static fromResults(validatorResults: readonly ValidatorCheckItem[], failOnWarning = false): CiCheckResult {
@@ -71,7 +85,7 @@ export class CiCheckResult {
71
85
  throw new Error("EmptyValidatorResultsError: validatorResults must have at least one item (INV-5)");
72
86
  }
73
87
  const allPassed = validatorResults.every((r) => isEffectivelyPassed(r, failOnWarning));
74
- return new CiCheckResult(validatorResults, allPassed);
88
+ return new CiCheckResult(validatorResults, allPassed, failOnWarning);
75
89
  }
76
90
 
77
91
  getFailedValidators(): readonly ValidatorCheckItem[] {