phasegate 0.254.0 → 0.283.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.
- package/CHANGELOG.md +60 -0
- package/README.ja.md +6 -3
- package/README.md +6 -3
- package/docs/contracts/attestation-v2.schema.json +110 -0
- package/docs/guide/cli-reference.md +12 -1
- package/docs/guide/configuration.md +56 -0
- package/docs/guide/installation.md +1 -1
- package/docs/templates/ci/aidlc-gate.yml +60 -2
- package/package.json +2 -2
- package/scripts/harness/agent-integration/application/dto/open-world-obligations-context-dto.ts +30 -0
- package/scripts/harness/agent-integration/application/ports/world-obligations-query-port.ts +30 -0
- package/scripts/harness/agent-integration/application/usecases/get-open-world-obligations-context-usecase.ts +66 -0
- package/scripts/harness/agent-integration/infrastructure/adapters/harness-config-config-query-adapter.ts +30 -27
- package/scripts/harness/agent-integration/infrastructure/adapters/world-model-open-obligations-query-adapter.ts +58 -0
- package/scripts/harness/agent-integration/presentation/post-tool-use-hook.ts +29 -16
- package/scripts/harness/agent-integration/presentation/session-start-hook.ts +37 -1
- package/scripts/harness/agent-integration/presentation/stop-hook.ts +35 -26
- package/scripts/harness/agent-integration/presentation/world-obligations-session-context.ts +60 -0
- package/scripts/harness/attestation/application/dto/attestation-document.ts +17 -4
- package/scripts/harness/attestation/application/mappers/attestation-record-mapper.ts +55 -4
- package/scripts/harness/attestation/application/ports/world-snapshot-root-provider.ts +10 -0
- package/scripts/harness/attestation/application/usecases/produce-attestation-usecase.ts +33 -7
- package/scripts/harness/attestation/composition-root.ts +5 -0
- package/scripts/harness/attestation/domain/entities/attestation-record.ts +37 -2
- package/scripts/harness/attestation/index.ts +7 -1
- package/scripts/harness/attestation/presentation/handlers/attest-handler.ts +5 -2
- package/scripts/harness/config-foundation/application/mappers/validator-system-config-mapper.ts +40 -9
- package/scripts/harness/config-foundation/application/mappers/world-model-config-mapper.ts +15 -0
- package/scripts/harness/config-foundation/domain/harness-config.ts +70 -87
- package/scripts/harness/config-foundation/domain/services/preset-resolution-service.ts +80 -88
- package/scripts/harness/config-foundation/domain/value-objects/project-config.ts +34 -18
- package/scripts/harness/config-foundation/domain/value-objects/world-config.ts +198 -0
- package/scripts/harness/config-foundation/index.ts +14 -15
- package/scripts/harness/config-foundation/infrastructure/presets/minimal.json +24 -0
- package/scripts/harness/config-foundation/infrastructure/presets/standard.json +24 -0
- package/scripts/harness/config-foundation/infrastructure/presets/strict.json +24 -0
- package/scripts/harness/config-foundation/infrastructure/repositories/file-system-config-repository.ts +27 -18
- package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v2.schema.json +84 -8
- package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v3.schema.json +84 -8
- package/scripts/harness/harness-api/domain/ports/config-query-port.ts +11 -1
- package/scripts/harness/harness-api/domain/services/command-dispatch-service.ts +125 -86
- package/scripts/harness/harness-api/domain/services/status-derivation-service.ts +31 -21
- package/scripts/harness/harness-api/domain/value-objects/ci-check-result.ts +18 -4
- package/scripts/harness/harness-api/domain/value-objects/harness-status-summary.ts +23 -8
- package/scripts/harness/harness-api/infrastructure/adapters/biome-ast-engine-lint-adapter.ts +4 -4
- package/scripts/harness/harness-api/infrastructure/adapters/harness-config-query-adapter.ts +79 -34
- package/scripts/harness/installation/application/usecases/run-install.ts +199 -51
- package/scripts/harness/installation/application/usecases/run-reconcile.ts +277 -69
- package/scripts/harness/installation/domain/deployment-manifest.ts +43 -0
- package/scripts/harness/integrations/pre-commit.ts +169 -27
- package/scripts/harness/main.ts +200 -136
- package/scripts/harness/traceability-model/application/dto/changed-design-fragment-dto.ts +31 -0
- package/scripts/harness/traceability-model/application/facades/design-change-read-facade.ts +14 -0
- package/scripts/harness/traceability-model/application/ports/staged-design-change-source-port.ts +9 -0
- package/scripts/harness/traceability-model/composition-root.ts +5 -0
- package/scripts/harness/traceability-model/index.ts +9 -0
- package/scripts/harness/traceability-model/infrastructure/adapters/git-staged-design-change-adapter.ts +155 -0
- package/scripts/harness/validator-system/application/use-cases/run-l2-validators-usecase.ts +31 -0
- package/scripts/harness/validator-system/application/use-cases/run-l3-validators-usecase.ts +57 -6
- package/scripts/harness/validator-system/composition-root.ts +25 -7
- package/scripts/harness/validator-system/domain/ports/ac-coverage-policy-port.ts +10 -1
- package/scripts/harness/validator-system/domain/ports/world-constraint-admission-policy-port.ts +28 -0
- package/scripts/harness/validator-system/domain/ports/world-constraint-rederivation-policy-port.ts +11 -0
- package/scripts/harness/validator-system/domain/services/design-change-declaration-policy.ts +75 -0
- package/scripts/harness/validator-system/domain/services/world-constraint-admission-service.ts +78 -0
- package/scripts/harness/validator-system/domain/services/world-constraint-rederivation-service.ts +76 -0
- package/scripts/harness/validator-system/domain/value-objects/validator-id.ts +4 -0
- package/scripts/harness/validator-system/infrastructure/adapters/harness-config-validator-config-adapter.ts +126 -18
- package/scripts/harness/validator-system/infrastructure/adapters/nyquist-ac-coverage-policy-adapter.ts +49 -20
- package/scripts/harness/validator-system/infrastructure/adapters/world-model-constraint-admission-adapter.ts +56 -0
- package/scripts/harness/validator-system/infrastructure/adapters/world-model-constraint-rederivation-adapter.ts +56 -0
- package/scripts/harness/world-model/application/dto/pinned-design-endpoint-dto.ts +19 -0
- package/scripts/harness/world-model/application/dto/world-resolved-config-input.ts +92 -27
- package/scripts/harness/world-model/application/dto/world-snapshot-root-dto.ts +8 -0
- package/scripts/harness/world-model/application/facades/pinned-design-endpoint-facade.ts +63 -0
- package/scripts/harness/world-model/application/facades/world-snapshot-root-facade.ts +19 -0
- package/scripts/harness/world-model/application/usecases/derive-world-obligations-use-case.ts +3 -1
- package/scripts/harness/world-model/composition-root.ts +62 -33
- package/scripts/harness/world-model/index.ts +11 -0
- package/scripts/harness/world-model/infrastructure/adapters/attestation-fact-extractor.ts +48 -13
- package/scripts/harness/world-model/infrastructure/adapters/file-system-world-control-repository-adapters.ts +6 -4
- package/scripts/harness/world-model/presentation/cli/world-derive-command-handler.ts +6 -2
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// @unit validator-system
|
|
2
|
+
// @layer infrastructure
|
|
3
|
+
// @work-item-id WI-301
|
|
4
|
+
|
|
5
|
+
import { createWorldModelModule, type WorldResolvedConfigInput } from "../../../world-model/index.js";
|
|
6
|
+
import type {
|
|
7
|
+
WorldConstraintAdmissionObservation,
|
|
8
|
+
WorldConstraintAdmissionPolicyPort,
|
|
9
|
+
} from "../../domain/ports/world-constraint-admission-policy-port.js";
|
|
10
|
+
|
|
11
|
+
interface Options {
|
|
12
|
+
readonly rootDir: string;
|
|
13
|
+
readonly resolvedConfig?: WorldResolvedConfigInput;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class WorldModelConstraintAdmissionAdapter implements WorldConstraintAdmissionPolicyPort {
|
|
17
|
+
constructor(private readonly options: Options) {}
|
|
18
|
+
|
|
19
|
+
async collect(): Promise<WorldConstraintAdmissionObservation> {
|
|
20
|
+
const module = createWorldModelModule({
|
|
21
|
+
rootDir: this.options.rootDir,
|
|
22
|
+
resolvedConfig: this.options.resolvedConfig,
|
|
23
|
+
});
|
|
24
|
+
const actual = await module.deriveWorldObligationsUseCase.execute({ writeReport: false });
|
|
25
|
+
const constraintsPath =
|
|
26
|
+
this.options.resolvedConfig?.declarations?.constraintsPath ?? "phasegate.world-constraints.json";
|
|
27
|
+
if (actual.status === "execution-failure") {
|
|
28
|
+
return Object.freeze({
|
|
29
|
+
obligations: Object.freeze([]),
|
|
30
|
+
diagnostics: Object.freeze(
|
|
31
|
+
actual.diagnostics.map((item) =>
|
|
32
|
+
Object.freeze({
|
|
33
|
+
code: item.code,
|
|
34
|
+
path: item.path,
|
|
35
|
+
message: item.message,
|
|
36
|
+
scope: item.path === constraintsPath ? ("constraint" as const) : ("other" as const),
|
|
37
|
+
}),
|
|
38
|
+
),
|
|
39
|
+
),
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
return Object.freeze({
|
|
43
|
+
obligations: Object.freeze(
|
|
44
|
+
actual.result.report.structuralObligations.map((item) =>
|
|
45
|
+
Object.freeze({
|
|
46
|
+
ruleId: item.ruleId,
|
|
47
|
+
violationFingerprint: item.violationFingerprint,
|
|
48
|
+
constraintId: item.constraintId,
|
|
49
|
+
classification: item.classification,
|
|
50
|
+
}),
|
|
51
|
+
),
|
|
52
|
+
),
|
|
53
|
+
diagnostics: Object.freeze([]),
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// @unit validator-system
|
|
2
|
+
// @layer infrastructure
|
|
3
|
+
// @work-item-id WI-302
|
|
4
|
+
|
|
5
|
+
import { createWorldModelModule, type WorldResolvedConfigInput } from "../../../world-model/index.js";
|
|
6
|
+
import type {
|
|
7
|
+
WorldConstraintRederivationObservation,
|
|
8
|
+
WorldConstraintRederivationPolicyPort,
|
|
9
|
+
} from "../../domain/ports/world-constraint-rederivation-policy-port.js";
|
|
10
|
+
|
|
11
|
+
interface Options {
|
|
12
|
+
readonly rootDir: string;
|
|
13
|
+
readonly resolvedConfig?: WorldResolvedConfigInput;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class WorldModelConstraintRederivationAdapter implements WorldConstraintRederivationPolicyPort {
|
|
17
|
+
constructor(private readonly options: Options) {}
|
|
18
|
+
|
|
19
|
+
async collect(): Promise<WorldConstraintRederivationObservation> {
|
|
20
|
+
const module = createWorldModelModule({
|
|
21
|
+
rootDir: this.options.rootDir,
|
|
22
|
+
resolvedConfig: this.options.resolvedConfig,
|
|
23
|
+
});
|
|
24
|
+
const actual = await module.deriveWorldObligationsUseCase.execute({ writeReport: false });
|
|
25
|
+
const constraintsPath =
|
|
26
|
+
this.options.resolvedConfig?.declarations?.constraintsPath ?? "phasegate.world-constraints.json";
|
|
27
|
+
if (actual.status === "execution-failure") {
|
|
28
|
+
return Object.freeze({
|
|
29
|
+
obligations: Object.freeze([]),
|
|
30
|
+
diagnostics: Object.freeze(
|
|
31
|
+
actual.diagnostics.map((item) =>
|
|
32
|
+
Object.freeze({
|
|
33
|
+
code: item.code,
|
|
34
|
+
path: item.path,
|
|
35
|
+
message: item.message,
|
|
36
|
+
scope: item.path === constraintsPath ? ("constraint" as const) : ("other" as const),
|
|
37
|
+
}),
|
|
38
|
+
),
|
|
39
|
+
),
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
return Object.freeze({
|
|
43
|
+
obligations: Object.freeze(
|
|
44
|
+
actual.result.report.structuralObligations.map((item) =>
|
|
45
|
+
Object.freeze({
|
|
46
|
+
ruleId: item.ruleId,
|
|
47
|
+
violationFingerprint: item.violationFingerprint,
|
|
48
|
+
constraintId: item.constraintId,
|
|
49
|
+
classification: item.classification,
|
|
50
|
+
}),
|
|
51
|
+
),
|
|
52
|
+
),
|
|
53
|
+
diagnostics: Object.freeze([]),
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// @unit world-model
|
|
2
|
+
// @layer application
|
|
3
|
+
// @work-item-id WI-305
|
|
4
|
+
|
|
5
|
+
export interface PinnedDesignEndpointDto {
|
|
6
|
+
readonly constraintId: string;
|
|
7
|
+
readonly endpoint: "claimant" | "premise";
|
|
8
|
+
readonly nodeId: string;
|
|
9
|
+
readonly corpusRole: "product" | "inception";
|
|
10
|
+
readonly declaredKey: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type PinnedDesignEndpointReadResultDto =
|
|
14
|
+
| {
|
|
15
|
+
readonly state: "available";
|
|
16
|
+
readonly endpoints: readonly PinnedDesignEndpointDto[];
|
|
17
|
+
readonly diagnosticCodes: readonly string[];
|
|
18
|
+
}
|
|
19
|
+
| { readonly state: "unavailable"; readonly diagnosticCodes: readonly string[] };
|
|
@@ -1,62 +1,127 @@
|
|
|
1
1
|
// @unit world-model
|
|
2
2
|
// @layer application
|
|
3
|
-
// @work-item-id WI-291
|
|
3
|
+
// @work-item-id WI-291, WI-300
|
|
4
4
|
|
|
5
5
|
import { PathKey } from "../../domain/value-objects/path-key.js";
|
|
6
6
|
|
|
7
7
|
export interface WorldResolvedConfigInput {
|
|
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?: { readonly obligationReportPath?: string };
|
|
29
|
+
readonly sessionStart?: {
|
|
30
|
+
readonly enabled?: boolean;
|
|
31
|
+
readonly maxItems?: number;
|
|
32
|
+
readonly maxChars?: number;
|
|
33
|
+
};
|
|
8
34
|
readonly designDocsRoot?: string;
|
|
9
35
|
readonly inceptionRoot?: string;
|
|
10
36
|
readonly requirementMatrixPath?: string;
|
|
11
37
|
}
|
|
12
38
|
|
|
13
39
|
export interface WorldCorpusConfig {
|
|
40
|
+
readonly enabled: boolean;
|
|
14
41
|
readonly productScopes: readonly {
|
|
15
42
|
readonly productRoot: string;
|
|
16
43
|
readonly designDocsRoot: string;
|
|
17
44
|
readonly unitRoot: string;
|
|
18
45
|
}[];
|
|
19
|
-
readonly
|
|
20
|
-
readonly
|
|
21
|
-
readonly
|
|
46
|
+
readonly inceptionRoots: readonly string[];
|
|
47
|
+
readonly adrRoots: readonly string[];
|
|
48
|
+
readonly sourceRoots: readonly string[];
|
|
22
49
|
readonly include: readonly string[];
|
|
23
50
|
readonly exclude: readonly string[];
|
|
24
51
|
readonly matrixPath: string;
|
|
25
52
|
readonly attestationPath: string;
|
|
26
53
|
readonly integrityManifestPath: string;
|
|
54
|
+
readonly constraintsPath: string;
|
|
55
|
+
readonly baselinePath: string;
|
|
56
|
+
readonly waiversPath: string;
|
|
57
|
+
readonly debtsPath: string;
|
|
58
|
+
readonly obligationReportPath: string;
|
|
59
|
+
readonly sessionStart: { readonly enabled: boolean; readonly maxItems: number; readonly maxChars: number };
|
|
27
60
|
}
|
|
28
61
|
|
|
29
|
-
const DEFAULT_DESIGN_DOCS_ROOT = "docs/product/construction";
|
|
30
|
-
|
|
31
62
|
const validatePath = (value: string): string => PathKey.create(value).toString();
|
|
63
|
+
const validatePaths = (values: readonly string[]): readonly string[] => Object.freeze(values.map(validatePath));
|
|
32
64
|
|
|
33
65
|
const deriveProductRoot = (designDocsRoot: string): string => {
|
|
34
66
|
const normalized = designDocsRoot.replace(/\/+$/, "");
|
|
35
67
|
return normalized.endsWith("/construction") ? normalized.slice(0, -"/construction".length) : normalized;
|
|
36
68
|
};
|
|
37
69
|
|
|
70
|
+
const resolveProductRoots = (input?: WorldResolvedConfigInput): readonly string[] => {
|
|
71
|
+
if (input?.corpus?.productRoots !== undefined) return validatePaths(input.corpus.productRoots);
|
|
72
|
+
if (input?.designDocsRoot === undefined) return Object.freeze(["docs/product"]);
|
|
73
|
+
const productRoot = validatePath(deriveProductRoot(input.designDocsRoot));
|
|
74
|
+
return Object.freeze(productRoot === "docs/product" ? [productRoot] : ["docs/product", productRoot]);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const assertLimit = (name: string, value: number, maximum: number): number => {
|
|
78
|
+
if (!Number.isInteger(value) || value < 1 || value > maximum) {
|
|
79
|
+
throw new Error(`${name} must be an integer between 1 and ${maximum}`);
|
|
80
|
+
}
|
|
81
|
+
return value;
|
|
82
|
+
};
|
|
83
|
+
|
|
38
84
|
export const resolveWorldCorpusConfig = (input?: WorldResolvedConfigInput): WorldCorpusConfig => {
|
|
39
|
-
const
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
85
|
+
const productRoots = resolveProductRoots(input);
|
|
86
|
+
const inceptionRoots = validatePaths(input?.corpus?.inceptionRoots ?? [input?.inceptionRoot ?? "docs/inception"]);
|
|
87
|
+
const adrRoots = validatePaths(input?.corpus?.adrRoots ?? ["docs/ADR"]);
|
|
88
|
+
const sourceRoots = validatePaths(input?.corpus?.sourceRoots ?? ["scripts/harness"]);
|
|
89
|
+
if ([productRoots, inceptionRoots, adrRoots, sourceRoots].some((roots) => roots.length === 0)) {
|
|
90
|
+
throw new Error("World corpus roots must not be empty");
|
|
91
|
+
}
|
|
92
|
+
const include = Object.freeze([...(input?.corpus?.include ?? ["**/*"])]);
|
|
93
|
+
const exclude = Object.freeze([...(input?.corpus?.exclude ?? [])]);
|
|
94
|
+
if (include.length === 0 || [...include, ...exclude].some((pattern) => pattern.length === 0)) {
|
|
95
|
+
throw new Error("World corpus selection patterns must not be empty");
|
|
96
|
+
}
|
|
51
97
|
return Object.freeze({
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
98
|
+
enabled: input?.enabled ?? false,
|
|
99
|
+
productScopes: Object.freeze(
|
|
100
|
+
productRoots.map((productRoot) => ({
|
|
101
|
+
productRoot,
|
|
102
|
+
designDocsRoot: validatePath(`${productRoot}/construction`),
|
|
103
|
+
unitRoot: validatePath(`${productRoot}/units`),
|
|
104
|
+
})),
|
|
105
|
+
),
|
|
106
|
+
inceptionRoots,
|
|
107
|
+
adrRoots,
|
|
108
|
+
sourceRoots,
|
|
109
|
+
include,
|
|
110
|
+
exclude,
|
|
111
|
+
matrixPath: validatePath(
|
|
112
|
+
input?.inputs?.matrixPath ?? input?.requirementMatrixPath ?? ".harness/requirement-test-matrix.json",
|
|
113
|
+
),
|
|
114
|
+
attestationPath: validatePath(input?.inputs?.attestationPath ?? ".harness/attestation.json"),
|
|
115
|
+
integrityManifestPath: validatePath(input?.inputs?.integrityManifestPath ?? "phasegate.integrity.json"),
|
|
116
|
+
constraintsPath: validatePath(input?.declarations?.constraintsPath ?? "phasegate.world-constraints.json"),
|
|
117
|
+
baselinePath: validatePath(input?.declarations?.baselinePath ?? "phasegate.world-baseline.json"),
|
|
118
|
+
waiversPath: validatePath(input?.declarations?.waiversPath ?? "phasegate.world-waivers.json"),
|
|
119
|
+
debtsPath: validatePath(input?.declarations?.debtsPath ?? "phasegate.world-debts.json"),
|
|
120
|
+
obligationReportPath: validatePath(input?.output?.obligationReportPath ?? ".harness/world-obligations.json"),
|
|
121
|
+
sessionStart: Object.freeze({
|
|
122
|
+
enabled: input?.sessionStart?.enabled ?? true,
|
|
123
|
+
maxItems: assertLimit("world.sessionStart.maxItems", input?.sessionStart?.maxItems ?? 5, 20),
|
|
124
|
+
maxChars: assertLimit("world.sessionStart.maxChars", input?.sessionStart?.maxChars ?? 2000, 8000),
|
|
125
|
+
}),
|
|
61
126
|
});
|
|
62
127
|
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// @unit world-model
|
|
2
|
+
// @layer application
|
|
3
|
+
// @work-item-id WI-305
|
|
4
|
+
|
|
5
|
+
import type { PinnedDesignEndpointDto, PinnedDesignEndpointReadResultDto } from "../dto/pinned-design-endpoint-dto.js";
|
|
6
|
+
import type { ConstraintDeclarationRepositoryPort } from "../ports/world-control-declaration-repository-port.js";
|
|
7
|
+
|
|
8
|
+
const EXPLICIT_FRAGMENT_ID = /^pgw:v1:fragment:(product|inception):(.+)$/;
|
|
9
|
+
const compare = (left: string, right: string): number => (left < right ? -1 : left > right ? 1 : 0);
|
|
10
|
+
|
|
11
|
+
export class PinnedDesignEndpointFacade {
|
|
12
|
+
constructor(private readonly repository: ConstraintDeclarationRepositoryPort) {}
|
|
13
|
+
|
|
14
|
+
async read(): Promise<PinnedDesignEndpointReadResultDto> {
|
|
15
|
+
const result = await this.repository.load();
|
|
16
|
+
if (result.state === "invalid") {
|
|
17
|
+
return Object.freeze({
|
|
18
|
+
state: "unavailable",
|
|
19
|
+
diagnosticCodes: Object.freeze([...new Set(result.diagnostics.map((item) => item.code))].sort(compare)),
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
if (result.value.malformedDeclarations.length > 0 || result.value.diagnostics.length > 0) {
|
|
23
|
+
return Object.freeze({
|
|
24
|
+
state: "unavailable",
|
|
25
|
+
diagnosticCodes: Object.freeze(
|
|
26
|
+
[
|
|
27
|
+
...(result.value.malformedDeclarations.length > 0 ? ["malformed-constraint-declaration"] : []),
|
|
28
|
+
...result.value.diagnostics.map((item) => item.code),
|
|
29
|
+
].sort(compare),
|
|
30
|
+
),
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
const endpoints: PinnedDesignEndpointDto[] = [];
|
|
34
|
+
for (const record of result.value.records) {
|
|
35
|
+
for (const endpoint of ["claimant", "premise"] as const) {
|
|
36
|
+
const nodeId = record[endpoint].nodeId.toString();
|
|
37
|
+
const match = EXPLICIT_FRAGMENT_ID.exec(nodeId);
|
|
38
|
+
if (!match) continue;
|
|
39
|
+
endpoints.push(
|
|
40
|
+
Object.freeze({
|
|
41
|
+
constraintId: record.constraintId.toString(),
|
|
42
|
+
endpoint,
|
|
43
|
+
nodeId,
|
|
44
|
+
corpusRole: match[1] as "product" | "inception",
|
|
45
|
+
declaredKey: match[2],
|
|
46
|
+
}),
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
endpoints.sort(
|
|
51
|
+
(left, right) =>
|
|
52
|
+
compare(left.corpusRole, right.corpusRole) ||
|
|
53
|
+
compare(left.declaredKey, right.declaredKey) ||
|
|
54
|
+
compare(left.constraintId, right.constraintId) ||
|
|
55
|
+
compare(left.endpoint, right.endpoint),
|
|
56
|
+
);
|
|
57
|
+
return Object.freeze({
|
|
58
|
+
state: "available",
|
|
59
|
+
endpoints: Object.freeze(endpoints),
|
|
60
|
+
diagnosticCodes: Object.freeze([]),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// @unit world-model
|
|
2
|
+
// @layer application
|
|
3
|
+
// @work-item-id WI-306
|
|
4
|
+
|
|
5
|
+
import type { WorldSnapshotRootDto } from "../dto/world-snapshot-root-dto.js";
|
|
6
|
+
import type { BuildSnapshotContract } from "../usecases/build-snapshot-use-case.js";
|
|
7
|
+
|
|
8
|
+
/** World domain型を公開せずcurrent corpusRootだけを返すpublic read facade。 */
|
|
9
|
+
export class WorldSnapshotRootFacade {
|
|
10
|
+
constructor(private readonly buildSnapshot: BuildSnapshotContract) {}
|
|
11
|
+
|
|
12
|
+
async read(): Promise<WorldSnapshotRootDto> {
|
|
13
|
+
const snapshot = await this.buildSnapshot.execute();
|
|
14
|
+
return {
|
|
15
|
+
schemaVersion: "phasegate-world-snapshot-root/v1",
|
|
16
|
+
worldSnapshotRoot: snapshot.corpusRoot.toString(),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
}
|
package/scripts/harness/world-model/application/usecases/derive-world-obligations-use-case.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// @unit world-model
|
|
2
2
|
// @layer application
|
|
3
3
|
// @work-item-id WI-296, WI-297
|
|
4
|
+
// @work-item-id WI-300
|
|
4
5
|
|
|
5
6
|
import { ExtractionDiagnostic } from "../../domain/entities/extraction-diagnostic.js";
|
|
6
7
|
import type { Snapshot } from "../../domain/entities/snapshot.js";
|
|
@@ -38,6 +39,7 @@ interface Dependencies {
|
|
|
38
39
|
readonly policyDate: PolicyDatePort;
|
|
39
40
|
readonly constraintConfigDigest: Sha256Digest;
|
|
40
41
|
readonly evaluationConfigDigest: Sha256Digest;
|
|
42
|
+
readonly constraintPath?: string;
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
const RULESET_VERSION = "phasegate-world-wcr/v1";
|
|
@@ -64,7 +66,7 @@ export class DeriveWorldObligationsUseCase {
|
|
|
64
66
|
...constraints.value.malformedDeclarations.map((declaration) =>
|
|
65
67
|
ExtractionDiagnostic.create({
|
|
66
68
|
code: "malformed-constraint-declaration",
|
|
67
|
-
path: PathKey.create("phasegate.world-constraints.json"),
|
|
69
|
+
path: PathKey.create(this.dependencies.constraintPath ?? "phasegate.world-constraints.json"),
|
|
68
70
|
payload: declaration.toCanonicalValue(),
|
|
69
71
|
}),
|
|
70
72
|
),
|
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
// @work-item-id WI-292
|
|
5
5
|
// @work-item-id WI-295
|
|
6
6
|
// @work-item-id WI-296, WI-297
|
|
7
|
+
// @work-item-id WI-300
|
|
8
|
+
// @work-item-id WI-305
|
|
9
|
+
// @work-item-id WI-306
|
|
7
10
|
|
|
8
11
|
import { createAttestationModule, createSha256Capability } from "../attestation/index.js";
|
|
9
12
|
import { createTraceabilityModelModule } from "../traceability-model/index.js";
|
|
@@ -11,6 +14,8 @@ import {
|
|
|
11
14
|
resolveWorldCorpusConfig,
|
|
12
15
|
type WorldResolvedConfigInput,
|
|
13
16
|
} from "./application/dto/world-resolved-config-input.js";
|
|
17
|
+
import { PinnedDesignEndpointFacade } from "./application/facades/pinned-design-endpoint-facade.js";
|
|
18
|
+
import { WorldSnapshotRootFacade } from "./application/facades/world-snapshot-root-facade.js";
|
|
14
19
|
import { BuildSnapshotUseCase } from "./application/usecases/build-snapshot-use-case.js";
|
|
15
20
|
import { DeriveObligationsUseCase } from "./application/usecases/derive-obligations-use-case.js";
|
|
16
21
|
import {
|
|
@@ -68,15 +73,17 @@ export function createWorldModelModule(options: WorldModelModuleOptions) {
|
|
|
68
73
|
const hashingPort = new AttestationSha256WorldHashingAdapter(createSha256Capability());
|
|
69
74
|
const serializer = new CanonicalJsonSerializer();
|
|
70
75
|
const markdownExtractor = new MarkdownDesignFactExtractor({ hashingPort });
|
|
71
|
-
const traceabilityFacades = config.productScopes.
|
|
72
|
-
(
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
76
|
+
const traceabilityFacades = config.productScopes.flatMap((scope) =>
|
|
77
|
+
config.inceptionRoots.map(
|
|
78
|
+
(inceptionRoot) =>
|
|
79
|
+
createTraceabilityModelModule(options.rootDir, {
|
|
80
|
+
pathRoots: {
|
|
81
|
+
designDocsRoot: scope.designDocsRoot,
|
|
82
|
+
inceptionRoot,
|
|
83
|
+
testRoots: config.sourceRoots.map((sourceRoot) => `${sourceRoot}/__tests__`),
|
|
84
|
+
},
|
|
85
|
+
}).worldReadFacade,
|
|
86
|
+
),
|
|
80
87
|
);
|
|
81
88
|
const designCorpus = new DesignCorpusFactExtractor({
|
|
82
89
|
traceabilityAdapter: new TraceabilityDesignFactAdapter({
|
|
@@ -94,16 +101,14 @@ export function createWorldModelModule(options: WorldModelModuleOptions) {
|
|
|
94
101
|
}),
|
|
95
102
|
),
|
|
96
103
|
),
|
|
97
|
-
proposalExtractor: new
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
adrExtractor: new
|
|
103
|
-
rootDir: options.rootDir,
|
|
104
|
-
|
|
105
|
-
adrRoot: config.adrRoot,
|
|
106
|
-
}),
|
|
104
|
+
proposalExtractor: new CompositeDesignFactSource(
|
|
105
|
+
config.inceptionRoots.map(
|
|
106
|
+
(inceptionRoot) => new ProposalFactExtractor({ rootDir: options.rootDir, markdownExtractor, inceptionRoot }),
|
|
107
|
+
),
|
|
108
|
+
),
|
|
109
|
+
adrExtractor: new CompositeDesignFactSource(
|
|
110
|
+
config.adrRoots.map((adrRoot) => new AdrFactExtractor({ rootDir: options.rootDir, markdownExtractor, adrRoot })),
|
|
111
|
+
),
|
|
107
112
|
unitExtractor: new CompositeDesignFactSource(
|
|
108
113
|
config.productScopes.map(
|
|
109
114
|
(scope) =>
|
|
@@ -118,16 +123,10 @@ export function createWorldModelModule(options: WorldModelModuleOptions) {
|
|
|
118
123
|
const attestationModule = createAttestationModule(options.rootDir);
|
|
119
124
|
const factSource = new AssembledWorldFactSource([
|
|
120
125
|
designCorpus,
|
|
121
|
-
|
|
122
|
-
rootDir: options.rootDir,
|
|
123
|
-
hashingPort,
|
|
124
|
-
|
|
125
|
-
}),
|
|
126
|
-
new TestReferenceSourceFactExtractor({
|
|
127
|
-
rootDir: options.rootDir,
|
|
128
|
-
hashingPort,
|
|
129
|
-
sourceRoot: config.sourceRoot,
|
|
130
|
-
}),
|
|
126
|
+
...config.sourceRoots.flatMap((sourceRoot) => [
|
|
127
|
+
new SourceMetadataFactExtractor({ rootDir: options.rootDir, hashingPort, sourceRoot }),
|
|
128
|
+
new TestReferenceSourceFactExtractor({ rootDir: options.rootDir, hashingPort, sourceRoot }),
|
|
129
|
+
]),
|
|
131
130
|
new MatrixFactExtractor({
|
|
132
131
|
rootDir: options.rootDir,
|
|
133
132
|
hashingPort,
|
|
@@ -148,7 +147,22 @@ export function createWorldModelModule(options: WorldModelModuleOptions) {
|
|
|
148
147
|
serializer,
|
|
149
148
|
}),
|
|
150
149
|
]);
|
|
151
|
-
const corpusConfigDigest = hashingPort.sha256(
|
|
150
|
+
const corpusConfigDigest = hashingPort.sha256(
|
|
151
|
+
serializer.serialize({
|
|
152
|
+
schemaVersion: "phasegate-world-corpus-config/v1",
|
|
153
|
+
productScopes: config.productScopes,
|
|
154
|
+
inceptionRoots: config.inceptionRoots,
|
|
155
|
+
adrRoots: config.adrRoots,
|
|
156
|
+
sourceRoots: config.sourceRoots,
|
|
157
|
+
include: config.include,
|
|
158
|
+
exclude: config.exclude,
|
|
159
|
+
inputs: {
|
|
160
|
+
matrixPath: config.matrixPath,
|
|
161
|
+
attestationPath: config.attestationPath,
|
|
162
|
+
integrityManifestPath: config.integrityManifestPath,
|
|
163
|
+
},
|
|
164
|
+
}),
|
|
165
|
+
);
|
|
152
166
|
const buildSnapshotUseCase = new BuildSnapshotUseCase({
|
|
153
167
|
factSource,
|
|
154
168
|
rootDeriver: new SnapshotRootDeriver(serializer, hashingPort),
|
|
@@ -158,26 +172,34 @@ export function createWorldModelModule(options: WorldModelModuleOptions) {
|
|
|
158
172
|
corpusConfigDigest,
|
|
159
173
|
});
|
|
160
174
|
const inspectWorldUseCase = new InspectWorldUseCase({ buildSnapshot: buildSnapshotUseCase });
|
|
175
|
+
const worldSnapshotRootFacade = new WorldSnapshotRootFacade(buildSnapshotUseCase);
|
|
161
176
|
const fingerprintDeriver = new ViolationFingerprintDeriver(serializer, hashingPort);
|
|
162
177
|
const constraintRepository = new FileSystemConstraintDeclarationRepositoryAdapter({
|
|
163
178
|
rootDir: options.rootDir,
|
|
179
|
+
fileName: config.constraintsPath,
|
|
164
180
|
});
|
|
165
181
|
const rootDeriver = new SnapshotRootDeriver(serializer, hashingPort);
|
|
166
182
|
const deriveObligationsUseCase = new DeriveObligationsUseCase({
|
|
167
183
|
baselineRepository: new FileSystemAdoptionBaselineRepositoryAdapter({
|
|
168
184
|
rootDir: options.rootDir,
|
|
185
|
+
fileName: config.baselinePath,
|
|
169
186
|
}),
|
|
170
187
|
waiverRepository: new FileSystemWaiverDeclarationRepositoryAdapter({
|
|
171
188
|
rootDir: options.rootDir,
|
|
189
|
+
fileName: config.waiversPath,
|
|
172
190
|
}),
|
|
173
191
|
semanticDebtRepository: new FileSystemSemanticDebtRepositoryAdapter({
|
|
174
192
|
rootDir: options.rootDir,
|
|
193
|
+
fileName: config.debtsPath,
|
|
175
194
|
}),
|
|
176
195
|
policyInputsDigestDeriver: new PolicyInputsDigestDeriver(serializer, hashingPort),
|
|
177
196
|
evaluationIdDeriver: new SnapshotRootDeriver(serializer, hashingPort),
|
|
178
197
|
obligationDerivationService: new ObligationDerivationService(fingerprintDeriver),
|
|
179
198
|
serializer,
|
|
180
|
-
writer: new FileSystemObligationReportWriterAdapter({
|
|
199
|
+
writer: new FileSystemObligationReportWriterAdapter({
|
|
200
|
+
rootDir: options.rootDir,
|
|
201
|
+
reportPath: config.obligationReportPath,
|
|
202
|
+
}),
|
|
181
203
|
});
|
|
182
204
|
const deriveWorldObligationsUseCase = new DeriveWorldObligationsUseCase({
|
|
183
205
|
buildSnapshot: buildSnapshotUseCase,
|
|
@@ -190,7 +212,7 @@ export function createWorldModelModule(options: WorldModelModuleOptions) {
|
|
|
190
212
|
constraintConfigDigest: hashingPort.sha256(
|
|
191
213
|
serializer.serialize({
|
|
192
214
|
schemaVersion: "phasegate-world-constraint-config/v1",
|
|
193
|
-
constraintsPath:
|
|
215
|
+
constraintsPath: config.constraintsPath,
|
|
194
216
|
}),
|
|
195
217
|
),
|
|
196
218
|
evaluationConfigDigest: hashingPort.sha256(
|
|
@@ -199,11 +221,16 @@ export function createWorldModelModule(options: WorldModelModuleOptions) {
|
|
|
199
221
|
rulesetVersion: "phasegate-world-wcr/v1",
|
|
200
222
|
}),
|
|
201
223
|
),
|
|
224
|
+
constraintPath: config.constraintsPath,
|
|
202
225
|
});
|
|
203
226
|
const pinConstraintEndpointUseCase = new PinConstraintEndpointUseCase(buildSnapshotUseCase, constraintRepository);
|
|
227
|
+
const pinnedDesignEndpointFacade = new PinnedDesignEndpointFacade(constraintRepository);
|
|
204
228
|
const worldInspectCommandHandler = new WorldInspectCommandHandler({ inspectWorld: inspectWorldUseCase });
|
|
205
229
|
const worldPinCommandHandler = new WorldPinCommandHandler(pinConstraintEndpointUseCase);
|
|
206
|
-
const worldDeriveCommandHandler = new WorldDeriveCommandHandler(
|
|
230
|
+
const worldDeriveCommandHandler = new WorldDeriveCommandHandler(
|
|
231
|
+
deriveWorldObligationsUseCase,
|
|
232
|
+
config.obligationReportPath,
|
|
233
|
+
);
|
|
207
234
|
|
|
208
235
|
return {
|
|
209
236
|
buildSnapshotUseCase,
|
|
@@ -211,8 +238,10 @@ export function createWorldModelModule(options: WorldModelModuleOptions) {
|
|
|
211
238
|
deriveWorldObligationsUseCase,
|
|
212
239
|
inspectWorldUseCase,
|
|
213
240
|
pinConstraintEndpointUseCase,
|
|
241
|
+
pinnedDesignEndpointFacade,
|
|
214
242
|
worldDeriveCommandHandler,
|
|
215
243
|
worldInspectCommandHandler,
|
|
244
|
+
worldSnapshotRootFacade,
|
|
216
245
|
worldPinCommandHandler,
|
|
217
246
|
} as const;
|
|
218
247
|
}
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
// @unit world-model
|
|
2
2
|
// @layer public-api
|
|
3
3
|
// @work-item-id WI-291, WI-296
|
|
4
|
+
// @work-item-id WI-304
|
|
5
|
+
// @work-item-id WI-305
|
|
6
|
+
// @work-item-id WI-306
|
|
4
7
|
|
|
8
|
+
export type {
|
|
9
|
+
PinnedDesignEndpointDto,
|
|
10
|
+
PinnedDesignEndpointReadResultDto,
|
|
11
|
+
} from "./application/dto/pinned-design-endpoint-dto.js";
|
|
5
12
|
export type {
|
|
6
13
|
WorldExtractionDiagnosticDto,
|
|
7
14
|
WorldInspectionDto,
|
|
@@ -9,10 +16,14 @@ export type {
|
|
|
9
16
|
WorldJsonObject,
|
|
10
17
|
WorldJsonValue,
|
|
11
18
|
} from "./application/dto/world-inspection-dto.js";
|
|
19
|
+
export type { WorldObligationReportDto } from "./application/dto/world-obligation-report-dto.js";
|
|
12
20
|
export type {
|
|
13
21
|
WorldCorpusConfig,
|
|
14
22
|
WorldResolvedConfigInput,
|
|
15
23
|
} from "./application/dto/world-resolved-config-input.js";
|
|
24
|
+
export type { WorldSnapshotRootDto } from "./application/dto/world-snapshot-root-dto.js";
|
|
25
|
+
export { PinnedDesignEndpointFacade } from "./application/facades/pinned-design-endpoint-facade.js";
|
|
26
|
+
export { WorldSnapshotRootFacade } from "./application/facades/world-snapshot-root-facade.js";
|
|
16
27
|
export type { WorldModelModuleOptions } from "./composition-root.js";
|
|
17
28
|
export { createWorldModelModule } from "./composition-root.js";
|
|
18
29
|
export type { WorldCommandResult } from "./presentation/cli/world-command-support.js";
|