@sdd-method/maintain-cli 0.0.0-stage → 0.1.0-rc.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/LICENSE +201 -0
- package/NOTICE +17 -0
- package/README.md +6 -2
- package/dist/lib/adr-origin.js +59 -0
- package/dist/lib/agent-harness.js +28 -0
- package/dist/lib/briefing/index.js +13 -0
- package/dist/lib/briefing/render.js +123 -0
- package/dist/lib/briefing/variables.js +92 -0
- package/dist/lib/bundle.js +123 -0
- package/dist/lib/catalogue/aggregate.js +2404 -0
- package/dist/lib/catalogue/aggregation-manifest-loader.js +173 -0
- package/dist/lib/catalogue/application-archetype/expectations.js +119 -0
- package/dist/lib/catalogue/application-archetype/vocabulary.js +177 -0
- package/dist/lib/catalogue/archetype/maps/android.yaml +66 -0
- package/dist/lib/catalogue/archetype/maps/swiftui.yaml +58 -0
- package/dist/lib/catalogue/archetype/maps/uikit.yaml +48 -0
- package/dist/lib/catalogue/archetype/maps/web-generic.yaml +86 -0
- package/dist/lib/catalogue/archetype/openui-archetype-vocabulary.yaml +46 -0
- package/dist/lib/catalogue/archetype/resolver.js +103 -0
- package/dist/lib/catalogue/archetype/wireframe.js +418 -0
- package/dist/lib/catalogue/baseline-check.js +151 -0
- package/dist/lib/catalogue/build.js +1502 -0
- package/dist/lib/catalogue/builders/adr-supersedes.js +103 -0
- package/dist/lib/catalogue/builders/adrs.js +222 -0
- package/dist/lib/catalogue/builders/application-conformance-coverage.js +47 -0
- package/dist/lib/catalogue/builders/application-data-plane-bindings.js +115 -0
- package/dist/lib/catalogue/builders/c4-components.js +229 -0
- package/dist/lib/catalogue/builders/c4-containers.js +240 -0
- package/dist/lib/catalogue/builders/c4-entity-rollups.js +87 -0
- package/dist/lib/catalogue/builders/c4-external-systems.js +194 -0
- package/dist/lib/catalogue/builders/c4-l1-candidates.js +107 -0
- package/dist/lib/catalogue/builders/c4-systems.js +98 -0
- package/dist/lib/catalogue/builders/capabilities.js +488 -0
- package/dist/lib/catalogue/builders/capability-domain-model.js +74 -0
- package/dist/lib/catalogue/builders/capability-edges.js +256 -0
- package/dist/lib/catalogue/builders/capability-spec-walker.js +99 -0
- package/dist/lib/catalogue/builders/capability-spec.js +348 -0
- package/dist/lib/catalogue/builders/container-dependency.js +183 -0
- package/dist/lib/catalogue/builders/contracts.js +402 -0
- package/dist/lib/catalogue/builders/data-items.js +344 -0
- package/dist/lib/catalogue/builders/data-store-mapping.js +113 -0
- package/dist/lib/catalogue/builders/domain-models.js +175 -0
- package/dist/lib/catalogue/builders/domains.js +119 -0
- package/dist/lib/catalogue/builders/edge-label-merge.js +19 -0
- package/dist/lib/catalogue/builders/entities-manifest.js +160 -0
- package/dist/lib/catalogue/builders/entities-mermaid.js +189 -0
- package/dist/lib/catalogue/builders/entity-persistence.js +102 -0
- package/dist/lib/catalogue/builders/entity-slug.js +47 -0
- package/dist/lib/catalogue/builders/event-catalog-walker.js +186 -0
- package/dist/lib/catalogue/builders/event-edges.js +327 -0
- package/dist/lib/catalogue/builders/events.js +168 -0
- package/dist/lib/catalogue/builders/evidence-class.js +43 -0
- package/dist/lib/catalogue/builders/evidence-target-repo.js +67 -0
- package/dist/lib/catalogue/builders/external-system-exemption.js +120 -0
- package/dist/lib/catalogue/builders/feature-contract.js +77 -0
- package/dist/lib/catalogue/builders/feature-domain-model.js +50 -0
- package/dist/lib/catalogue/builders/feature-grounding-walker.js +254 -0
- package/dist/lib/catalogue/builders/feature-grounding.js +391 -0
- package/dist/lib/catalogue/builders/feature-groups.js +63 -0
- package/dist/lib/catalogue/builders/feature-subscription-channels.js +228 -0
- package/dist/lib/catalogue/builders/feature-tree-walker.js +295 -0
- package/dist/lib/catalogue/builders/features.js +300 -0
- package/dist/lib/catalogue/builders/glossary-xref.js +378 -0
- package/dist/lib/catalogue/builders/glossary.js +204 -0
- package/dist/lib/catalogue/builders/implementations.js +214 -0
- package/dist/lib/catalogue/builders/index.js +64 -0
- package/dist/lib/catalogue/builders/integration-components.js +344 -0
- package/dist/lib/catalogue/builders/integrations.js +181 -0
- package/dist/lib/catalogue/builders/persona-c4system.js +149 -0
- package/dist/lib/catalogue/builders/persona-container.js +133 -0
- package/dist/lib/catalogue/builders/persona-journey-edges.js +133 -0
- package/dist/lib/catalogue/builders/personas.js +152 -0
- package/dist/lib/catalogue/builders/product-edges.js +106 -0
- package/dist/lib/catalogue/builders/product-platform-dependencies.js +405 -0
- package/dist/lib/catalogue/builders/products.js +162 -0
- package/dist/lib/catalogue/builders/quality-scores.js +635 -0
- package/dist/lib/catalogue/builders/repo-capability-exemption.js +61 -0
- package/dist/lib/catalogue/builders/repo-dependency.js +127 -0
- package/dist/lib/catalogue/builders/repo-edges.js +340 -0
- package/dist/lib/catalogue/builders/repositories.js +458 -0
- package/dist/lib/catalogue/builders/scaffolds.js +197 -0
- package/dist/lib/catalogue/builders/screen-tree.js +222 -0
- package/dist/lib/catalogue/builders/screens.js +396 -0
- package/dist/lib/catalogue/builders/service-capability-exemption.js +63 -0
- package/dist/lib/catalogue/builders/service-contract-consumes.js +186 -0
- package/dist/lib/catalogue/builders/service-contract.js +74 -0
- package/dist/lib/catalogue/builders/service-entity-tables.js +187 -0
- package/dist/lib/catalogue/builders/services.js +232 -0
- package/dist/lib/catalogue/builders/system-external-dependency.js +115 -0
- package/dist/lib/catalogue/builders/user-journeys.js +253 -0
- package/dist/lib/catalogue/canonical-schema.yaml +4117 -0
- package/dist/lib/catalogue/cli-version.js +10 -0
- package/dist/lib/catalogue/contract-id.js +49 -0
- package/dist/lib/catalogue/contract-path-resolution.js +149 -0
- package/dist/lib/catalogue/coverage-predicates.js +603 -0
- package/dist/lib/catalogue/coverage-render.js +552 -0
- package/dist/lib/catalogue/coverage-run.js +115 -0
- package/dist/lib/catalogue/csv-reader.js +97 -0
- package/dist/lib/catalogue/csv-writer.js +96 -0
- package/dist/lib/catalogue/data-item-coverage.js +142 -0
- package/dist/lib/catalogue/data-plane-coherence.js +77 -0
- package/dist/lib/catalogue/ddl-generator.js +233 -0
- package/dist/lib/catalogue/dedupe-keyed-tables.js +0 -0
- package/dist/lib/catalogue/engine-validator.js +239 -0
- package/dist/lib/catalogue/ext-id.js +76 -0
- package/dist/lib/catalogue/feature-grounding-aliases-loader.js +65 -0
- package/dist/lib/catalogue/feature-repo-zero-resolution-summary.js +31 -0
- package/dist/lib/catalogue/finding-identity.js +183 -0
- package/dist/lib/catalogue/findings.js +46 -0
- package/dist/lib/catalogue/fk-validator.js +136 -0
- package/dist/lib/catalogue/freshness.js +69 -0
- package/dist/lib/catalogue/gate-checks.js +338 -0
- package/dist/lib/catalogue/graph-projection.js +346 -0
- package/dist/lib/catalogue/graph-run.js +64 -0
- package/dist/lib/catalogue/impact-run.js +228 -0
- package/dist/lib/catalogue/manifest-loader.js +435 -0
- package/dist/lib/catalogue/platform-scope.js +7 -0
- package/dist/lib/catalogue/population-checks.js +87 -0
- package/dist/lib/catalogue/relationship-semantics.js +66 -0
- package/dist/lib/catalogue/reverse-product-quality.js +99 -0
- package/dist/lib/catalogue/rubric-coverage-map.yaml +871 -0
- package/dist/lib/catalogue/runway-vocabulary-loader.js +56 -0
- package/dist/lib/catalogue/scaffold-delta.js +261 -0
- package/dist/lib/catalogue/schema-compat.js +52 -0
- package/dist/lib/catalogue/schema-loader.js +87 -0
- package/dist/lib/catalogue/scores-coverage.js +573 -0
- package/dist/lib/catalogue/screen-validation-loader.js +89 -0
- package/dist/lib/catalogue/service-architecture-parser.js +193 -0
- package/dist/lib/catalogue/service-contract-aliases-loader.js +109 -0
- package/dist/lib/catalogue/staleness-verdicts.js +345 -0
- package/dist/lib/catalogue/traceability-loader.js +350 -0
- package/dist/lib/catalogue/validate.js +200 -0
- package/dist/lib/catalogue/version.js +18 -0
- package/dist/lib/ci-binding/index.js +209 -0
- package/dist/lib/ci-binding/module.js +204 -0
- package/dist/lib/conformance/binding.js +70 -0
- package/dist/lib/context-pull/index.js +151 -0
- package/dist/lib/context-pull/resolve.js +70 -0
- package/dist/lib/generate-briefing/index.js +77 -0
- package/dist/lib/hooks-seed/copilot.js +167 -0
- package/dist/lib/hooks-seed/entries.js +47 -0
- package/dist/lib/hooks-seed/index.js +17 -0
- package/dist/lib/hooks-seed/lifecycle-mapping.js +28 -0
- package/dist/lib/hooks-seed/merge.js +128 -0
- package/dist/lib/hooks-seed/types.js +20 -0
- package/dist/lib/init/ci-workflow.js +89 -0
- package/dist/lib/list/discover.js +100 -0
- package/dist/lib/list/format.js +84 -0
- package/dist/lib/list/index.js +16 -0
- package/dist/lib/lock-file.js +432 -0
- package/dist/lib/mcp-seed/entries.js +14 -0
- package/dist/lib/mcp-seed/index.js +28 -0
- package/dist/lib/mcp-seed/merge.js +99 -0
- package/dist/lib/mcp-seed/types.js +13 -0
- package/dist/lib/pack/builder.js +303 -0
- package/dist/lib/pack/index.js +87 -0
- package/dist/lib/pack/leakage.js +144 -0
- package/dist/lib/pack/log.js +18 -0
- package/dist/lib/pack/publish-transform.js +233 -0
- package/dist/lib/pack/reference-closure.js +125 -0
- package/dist/lib/pack/schema.js +121 -0
- package/dist/lib/pack/seed.js +182 -0
- package/dist/lib/pack/validate.js +49 -0
- package/dist/lib/package-version.js +92 -0
- package/dist/lib/repo-kind.js +129 -0
- package/dist/lib/scripts.js +7 -0
- package/dist/lib/snapshot/catalogue-diff.js +219 -0
- package/dist/lib/snapshot/manifest.js +206 -0
- package/dist/lib/sync/adr-transitions.js +250 -0
- package/dist/lib/sync/apply.js +91 -0
- package/dist/lib/sync/briefing-merge.js +90 -0
- package/dist/lib/sync/bundle-cache.js +109 -0
- package/dist/lib/sync/bundle-source.js +61 -0
- package/dist/lib/sync/config.js +72 -0
- package/dist/lib/sync/copilot-hooks-merge.js +108 -0
- package/dist/lib/sync/diff.js +251 -0
- package/dist/lib/sync/index.js +809 -0
- package/dist/lib/sync/settings-merge.js +216 -0
- package/dist/lib/sync/yaml-block-merge.js +120 -0
- package/dist/lib/validate/aggregate.js +51 -0
- package/dist/lib/validate/bundle-coherence.js +74 -0
- package/dist/lib/validate/dispatch.js +243 -0
- package/dist/lib/validate/index.js +110 -0
- package/dist/lib/validate/plan.js +54 -0
- package/dist/lib/validate/runner.js +85 -0
- package/dist/lib/validate/yaml-parse.js +112 -0
- package/dist/maintain-bin.js +8 -0
- package/dist/maintain-program.js +91 -0
- package/dist/maintain-version.json +4 -0
- package/dist/verbs/catalogue-maintain.js +110 -0
- package/dist/verbs/catalogue-verify.js +114 -0
- package/dist/verbs/context.js +39 -0
- package/dist/verbs/generate-briefing.js +29 -0
- package/dist/verbs/list.js +45 -0
- package/dist/verbs/pack-build.js +31 -0
- package/dist/verbs/pack-validate.js +17 -0
- package/dist/verbs/path-option.js +25 -0
- package/dist/verbs/render.js +87 -0
- package/dist/verbs/sync.js +98 -0
- package/dist/verbs/validate.js +48 -0
- package/dist/verify-program.js +122 -0
- package/dist/verify-version.json +4 -0
- package/package.json +32 -4
- package/scripts/validate-app-dependencies.sh +664 -0
- package/scripts/validate-app-docs.sh +199 -0
- package/scripts/validate-integration-docs.sh +505 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
// Aggregation-manifest loader per
|
|
2
|
+
// docs/method/methods/aggregation-manifest-schema.yaml.
|
|
3
|
+
//
|
|
4
|
+
// v1 supports `pull.kind: filesystem` only; other kinds (git-ref,
|
|
5
|
+
// release-asset, mcp-resource) are schema-reserved and reject with a
|
|
6
|
+
// clear "deferred" error so adopters get unambiguous feedback.
|
|
7
|
+
import { readFile } from "node:fs/promises";
|
|
8
|
+
import { isAbsolute, relative, resolve } from "node:path";
|
|
9
|
+
import { parse as parseYaml } from "yaml";
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
import { SCHEMA_COMPAT, formatSchemaVersionMismatch } from "./schema-compat.js";
|
|
12
|
+
import { isInRange } from "./manifest-loader.js";
|
|
13
|
+
export const DEFAULT_AGGREGATION_MANIFEST_REL = "orchestration/aggregation-manifest.yaml";
|
|
14
|
+
const PullSchema = z
|
|
15
|
+
.object({
|
|
16
|
+
kind: z.string(),
|
|
17
|
+
path: z.string().optional(),
|
|
18
|
+
})
|
|
19
|
+
.passthrough();
|
|
20
|
+
const SiblingSchema = z.object({
|
|
21
|
+
sdd_id: z.string().regex(/^[a-z][a-z0-9-]*$/, {
|
|
22
|
+
message: "sdd_id must match /^[a-z][a-z0-9-]*$/",
|
|
23
|
+
}),
|
|
24
|
+
pull: PullSchema,
|
|
25
|
+
description: z.string().optional(),
|
|
26
|
+
level: z.string().optional(),
|
|
27
|
+
});
|
|
28
|
+
/**
|
|
29
|
+
* An ecosystem member that is real and intended but has no SDD yet
|
|
30
|
+
* (aggregation-manifest-schema 1.1.0, per M-ADR 0195 §4.3).
|
|
31
|
+
*
|
|
32
|
+
* A SEPARATE list rather than a flag on `siblings[]`: `pull` is required on a sibling
|
|
33
|
+
* and a pending member has nothing to pull, and `siblings[]` must keep meaning exactly
|
|
34
|
+
* "members whose catalogues merge in" — adding skippable entries would give one list
|
|
35
|
+
* two meanings, which every existing reader would keep miscounting.
|
|
36
|
+
*
|
|
37
|
+
* `reason` is required and non-empty. A bare marker suppresses the build failure while
|
|
38
|
+
* saying nothing about why, losing the signal that a pending member is a WORK ITEM —
|
|
39
|
+
* the state this replaces.
|
|
40
|
+
*/
|
|
41
|
+
const PendingMemberSchema = z.object({
|
|
42
|
+
sdd_id: z.string().regex(/^[a-z][a-z0-9-]*$/, {
|
|
43
|
+
message: "pending_members[].sdd_id must match /^[a-z][a-z0-9-]*$/",
|
|
44
|
+
}),
|
|
45
|
+
reason: z.string().min(1, {
|
|
46
|
+
message: "pending_members[].reason must be non-empty — a marker without a reason suppresses a failure without explaining it",
|
|
47
|
+
}),
|
|
48
|
+
blocked_on: z.string().optional(),
|
|
49
|
+
});
|
|
50
|
+
const SLUG = /^[a-z][a-z0-9-]*$/;
|
|
51
|
+
// M-ADR 0208 §4.3: `root_sdd_id` is canonical; `ecosystem_root` is its DEPRECATED alias
|
|
52
|
+
// (at organisation tier the old name called an organisation-root an "ecosystem root").
|
|
53
|
+
// Exactly one of the two must be present. Removed at 2.0.0.
|
|
54
|
+
const AggregationManifestSchema = z
|
|
55
|
+
.object({
|
|
56
|
+
schema_version: z.string(),
|
|
57
|
+
root_sdd_id: z.string().regex(SLUG).optional(),
|
|
58
|
+
ecosystem_root: z.string().regex(SLUG).optional(),
|
|
59
|
+
level: z.enum(["platform", "organisation"]).optional(),
|
|
60
|
+
siblings: z.array(SiblingSchema),
|
|
61
|
+
pending_members: z.array(PendingMemberSchema).optional(),
|
|
62
|
+
})
|
|
63
|
+
.refine((m) => (m.root_sdd_id !== undefined) !== (m.ecosystem_root !== undefined), {
|
|
64
|
+
message: "exactly one of `root_sdd_id` (canonical, M-ADR 0208 §4.3) or `ecosystem_root` (deprecated alias) must be present",
|
|
65
|
+
});
|
|
66
|
+
export async function loadAggregationManifest(manifestPath, options) {
|
|
67
|
+
let text;
|
|
68
|
+
try {
|
|
69
|
+
text = await readFile(manifestPath, "utf-8");
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
if (err.code === "ENOENT") {
|
|
73
|
+
throw new Error(`Aggregation manifest not found at ${manifestPath}\n\n` +
|
|
74
|
+
`Ecosystem-root SDDs declare an aggregation manifest at the path\n` +
|
|
75
|
+
`recorded in .sdd/sdd-manifest.yaml#aggregation_manifest_path, or\n` +
|
|
76
|
+
`at ${DEFAULT_AGGREGATION_MANIFEST_REL} by default.\n` +
|
|
77
|
+
`Schema: docs/method/methods/aggregation-manifest-schema.yaml.`);
|
|
78
|
+
}
|
|
79
|
+
throw err;
|
|
80
|
+
}
|
|
81
|
+
let raw;
|
|
82
|
+
try {
|
|
83
|
+
raw = parseYaml(text);
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
throw new Error(`failed to parse YAML at ${manifestPath}: ${err.message}`);
|
|
87
|
+
}
|
|
88
|
+
const parsed = AggregationManifestSchema.safeParse(raw);
|
|
89
|
+
if (!parsed.success) {
|
|
90
|
+
const issues = parsed.error.issues
|
|
91
|
+
.map((i) => ` - ${i.path.join(".") || "<root>"}: ${i.message}`)
|
|
92
|
+
.join("\n");
|
|
93
|
+
throw new Error(`invalid aggregation manifest at ${manifestPath}:\n${issues}`);
|
|
94
|
+
}
|
|
95
|
+
if (!isInRange(parsed.data.schema_version, SCHEMA_COMPAT.aggregation_manifest)) {
|
|
96
|
+
throw new Error(formatSchemaVersionMismatch({
|
|
97
|
+
schema: "aggregation_manifest",
|
|
98
|
+
readVersion: parsed.data.schema_version,
|
|
99
|
+
cliVersion: options.cliVersion,
|
|
100
|
+
sourcePath: manifestPath,
|
|
101
|
+
}));
|
|
102
|
+
}
|
|
103
|
+
// Duplicate sibling-id check.
|
|
104
|
+
const seen = new Set();
|
|
105
|
+
for (const s of parsed.data.siblings) {
|
|
106
|
+
if (seen.has(s.sdd_id)) {
|
|
107
|
+
throw new Error(`aggregation manifest has duplicate sibling sdd_id "${s.sdd_id}"\n` +
|
|
108
|
+
` (${manifestPath})`);
|
|
109
|
+
}
|
|
110
|
+
seen.add(s.sdd_id);
|
|
111
|
+
}
|
|
112
|
+
// Resolve each sibling's pull. v1 supports `kind: filesystem` only.
|
|
113
|
+
const resolved = [];
|
|
114
|
+
for (const s of parsed.data.siblings) {
|
|
115
|
+
const kind = s.pull.kind;
|
|
116
|
+
if (kind !== "filesystem") {
|
|
117
|
+
throw new Error(`aggregation manifest: sibling "${s.sdd_id}" uses pull.kind="${kind}"\n` +
|
|
118
|
+
` v1 supports filesystem only. Other kinds (git-ref, release-asset,\n` +
|
|
119
|
+
` mcp-resource) are schema-reserved and not yet implemented.\n` +
|
|
120
|
+
` See: docs/method/methods/aggregation-manifest-schema.yaml#pull_kinds`);
|
|
121
|
+
}
|
|
122
|
+
const path = s.pull.path;
|
|
123
|
+
if (!path || typeof path !== "string") {
|
|
124
|
+
throw new Error(`aggregation manifest: sibling "${s.sdd_id}" pull.kind=filesystem requires \`path\``);
|
|
125
|
+
}
|
|
126
|
+
const cataloguesPath = isAbsolute(path)
|
|
127
|
+
? path
|
|
128
|
+
: resolve(options.ecosystemRootPath, path);
|
|
129
|
+
// The sibling's repo root is the parent of `catalogues/`.
|
|
130
|
+
const sddRoot = resolve(cataloguesPath, "..");
|
|
131
|
+
// Portable form for the committed JSON snapshot: keep a relative source
|
|
132
|
+
// verbatim; anchor an absolute source to the ecosystem root so the emitted
|
|
133
|
+
// value never carries a machine-specific $HOME/checkout prefix.
|
|
134
|
+
const relativeCataloguesPath = isAbsolute(path)
|
|
135
|
+
? relative(options.ecosystemRootPath, cataloguesPath)
|
|
136
|
+
: path;
|
|
137
|
+
resolved.push({
|
|
138
|
+
sddId: s.sdd_id,
|
|
139
|
+
description: s.description ?? "",
|
|
140
|
+
cataloguesPath,
|
|
141
|
+
relativeCataloguesPath,
|
|
142
|
+
sddRoot,
|
|
143
|
+
level: s.level,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
// A member is pending authoring OR contributing catalogues, never both. Declaring
|
|
147
|
+
// both is a contradiction rather than something to merge, and silently preferring
|
|
148
|
+
// one reading would make the work queue untrustworthy — so it fails with the id.
|
|
149
|
+
const pendingMembers = [];
|
|
150
|
+
const siblingIds = new Set(resolved.map((r) => r.sddId));
|
|
151
|
+
for (const p of parsed.data.pending_members ?? []) {
|
|
152
|
+
if (siblingIds.has(p.sdd_id)) {
|
|
153
|
+
throw new Error(`aggregation manifest: "${p.sdd_id}" appears in BOTH siblings and pending_members.\n` +
|
|
154
|
+
` A member is either pending authoring or contributing catalogues, not both.\n` +
|
|
155
|
+
` Remove whichever declaration is stale (${parsed.data.root_sdd_id ?? parsed.data.ecosystem_root}).`);
|
|
156
|
+
}
|
|
157
|
+
pendingMembers.push({
|
|
158
|
+
sddId: p.sdd_id,
|
|
159
|
+
reason: p.reason,
|
|
160
|
+
...(p.blocked_on !== undefined ? { blockedOn: p.blocked_on } : {}),
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
schemaVersion: parsed.data.schema_version,
|
|
165
|
+
ecosystemRoot: (parsed.data.root_sdd_id ?? parsed.data.ecosystem_root),
|
|
166
|
+
level: parsed.data.level,
|
|
167
|
+
rootFieldDeprecated: parsed.data.root_sdd_id === undefined,
|
|
168
|
+
siblings: resolved,
|
|
169
|
+
pendingMembers,
|
|
170
|
+
sourcePath: manifestPath,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=aggregation-manifest-loader.js.map
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// Archetype expectations overlay — the deviation-visible WARN (M-ADR 0191 §4.3,
|
|
2
|
+
// given concrete content by M-ADR 0193 §4.4).
|
|
3
|
+
//
|
|
4
|
+
// An archetype supplies a DEFAULT data-plane composition (its `expected_bindings`
|
|
5
|
+
// in the reference-architecture vocabulary). A product's declared bindings are
|
|
6
|
+
// checked against that default:
|
|
7
|
+
// - a `required` expected pattern with no declared binding of that pattern, OR
|
|
8
|
+
// - a declared binding whose pattern is `absent-by-shape` for the archetype,
|
|
9
|
+
// is a deviation. A deviation is a WARN (`convention` finding) UNLESS the product
|
|
10
|
+
// declares a matching `data_plane.deviations` rationale — a declared deviation is
|
|
11
|
+
// sanctioned variance, not silent drift.
|
|
12
|
+
//
|
|
13
|
+
// `typical` and `optional` expectations are advisory only: neither presence nor
|
|
14
|
+
// absence warns. The check runs only when the archetype's overlay is discoverable
|
|
15
|
+
// (the reference-architecture bundle is consumed); absence of an overlay is a
|
|
16
|
+
// silent skip, never a coupling to bundle presence (M-ADR 0191 §4.1).
|
|
17
|
+
import { UNCLASSIFIED } from "./vocabulary.js";
|
|
18
|
+
/**
|
|
19
|
+
* Emit a deviation-visible WARN for each undeclared departure from the
|
|
20
|
+
* archetype's default composition. Returns the number of WARNs emitted.
|
|
21
|
+
*/
|
|
22
|
+
export function checkDataPlaneExpectations(inputs) {
|
|
23
|
+
const { productId, archetype, expectedBindings, declaredBindings, deviations, findings } = inputs;
|
|
24
|
+
const declaredPatterns = new Set(declaredBindings.map((b) => b.pattern));
|
|
25
|
+
const covered = new Set(deviations.map((d) => d.expectation));
|
|
26
|
+
let warned = 0;
|
|
27
|
+
for (const expected of expectedBindings) {
|
|
28
|
+
if (covered.has(expected.pattern))
|
|
29
|
+
continue; // declared deviation — sanctioned
|
|
30
|
+
if (expected.presence === "required" && !declaredPatterns.has(expected.pattern)) {
|
|
31
|
+
findings.add("convention", `data_plane: archetype "${archetype}" expects a required "${expected.pattern}" binding for ${productId}, ` +
|
|
32
|
+
`but none is declared and no data_plane.deviations rationale covers it (M-ADR 0191 §4.3)`);
|
|
33
|
+
warned++;
|
|
34
|
+
}
|
|
35
|
+
else if (expected.presence === "absent-by-shape" && declaredPatterns.has(expected.pattern)) {
|
|
36
|
+
findings.add("convention", `data_plane: archetype "${archetype}" is not expected to carry a "${expected.pattern}" binding ` +
|
|
37
|
+
`(absent-by-shape), but ${productId} declares one and no data_plane.deviations rationale covers it ` +
|
|
38
|
+
`(M-ADR 0191 §4.3)`);
|
|
39
|
+
warned++;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return warned;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Run the M-ADR 0191 §4.3 overlay at AGGREGATION tier — the deviation-visible
|
|
46
|
+
* WARN and the conformance surface — across every application-product. This is
|
|
47
|
+
* the tier where the reference-architecture bundle (and its `expected_bindings`)
|
|
48
|
+
* resolves; a per-SDD run would be inert for an SDD that consumes an adopter
|
|
49
|
+
* context-pack rather than the RA bundle (M-ADR 0191 §4.1; the 0.92.1 membership
|
|
50
|
+
* move). An archetype with no discoverable overlay is silently skipped.
|
|
51
|
+
*/
|
|
52
|
+
export function checkDataPlaneOverlayAtAggregation(inputs) {
|
|
53
|
+
const { products, bindings, deviations, conformanceCoverage, vocab, findings } = inputs;
|
|
54
|
+
const bindingsByProduct = new Map();
|
|
55
|
+
for (const b of bindings) {
|
|
56
|
+
const pid = String(b["product_id"] ?? "");
|
|
57
|
+
if (pid === "")
|
|
58
|
+
continue;
|
|
59
|
+
(bindingsByProduct.get(pid) ?? bindingsByProduct.set(pid, []).get(pid)).push({
|
|
60
|
+
pattern: String(b["pattern"] ?? ""),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
const deviationsByProduct = new Map();
|
|
64
|
+
for (const d of deviations) {
|
|
65
|
+
const pid = String(d["product_id"] ?? "");
|
|
66
|
+
if (pid === "")
|
|
67
|
+
continue;
|
|
68
|
+
(deviationsByProduct.get(pid) ?? deviationsByProduct.set(pid, []).get(pid)).push({
|
|
69
|
+
expectation: String(d["expectation"] ?? ""),
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
// Conformance coverage: per product, the set of tags declared covered or
|
|
73
|
+
// deviated (both satisfy the gate — deviated is sanctioned variance).
|
|
74
|
+
const conformanceByProduct = new Map();
|
|
75
|
+
for (const c of conformanceCoverage) {
|
|
76
|
+
const pid = String(c["product_id"] ?? "");
|
|
77
|
+
const tag = String(c["tag"] ?? "");
|
|
78
|
+
if (pid === "" || tag === "")
|
|
79
|
+
continue;
|
|
80
|
+
(conformanceByProduct.get(pid) ?? conformanceByProduct.set(pid, new Set()).get(pid)).add(tag);
|
|
81
|
+
}
|
|
82
|
+
for (const p of products) {
|
|
83
|
+
if (String(p["profile"] ?? "") !== "application-product")
|
|
84
|
+
continue;
|
|
85
|
+
const archetype = String(p["application_archetype"] ?? "");
|
|
86
|
+
if (archetype === "" || archetype === UNCLASSIFIED)
|
|
87
|
+
continue;
|
|
88
|
+
const productId = String(p["id"] ?? "");
|
|
89
|
+
if (productId === "")
|
|
90
|
+
continue;
|
|
91
|
+
const term = vocab.terms.find((t) => t.id === archetype);
|
|
92
|
+
if (term === undefined)
|
|
93
|
+
continue;
|
|
94
|
+
if (term.expectedBindings !== undefined && term.expectedBindings.length > 0) {
|
|
95
|
+
checkDataPlaneExpectations({
|
|
96
|
+
productId,
|
|
97
|
+
archetype,
|
|
98
|
+
expectedBindings: term.expectedBindings,
|
|
99
|
+
declaredBindings: bindingsByProduct.get(productId) ?? [],
|
|
100
|
+
deviations: deviationsByProduct.get(productId) ?? [],
|
|
101
|
+
findings,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
// Conformance GATE (M-ADR 0191 §4.3 WS4): each archetype-expected tag must be
|
|
105
|
+
// declared covered OR deviated; an uncovered, undeviated tag is a
|
|
106
|
+
// deviation-visible finding. Satisfied tags are silent.
|
|
107
|
+
if (term.conformanceExpectations !== undefined && term.conformanceExpectations.length > 0) {
|
|
108
|
+
const declared = conformanceByProduct.get(productId) ?? new Set();
|
|
109
|
+
for (const tag of term.conformanceExpectations) {
|
|
110
|
+
if (declared.has(tag))
|
|
111
|
+
continue; // covered or deviated — satisfied
|
|
112
|
+
findings.add("coverage", `conformance: archetype "${archetype}" expects the "${tag}" conformance for ${productId}, ` +
|
|
113
|
+
`but it is neither declared covered (conformance.coverage) nor deviated (conformance.deviations) ` +
|
|
114
|
+
`— assert coverage or declare why it does not apply (M-ADR 0191 §4.3)`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=expectations.js.map
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Application-archetype vocabulary resolution (M-ADR 0191 §4.2).
|
|
3
|
+
*
|
|
4
|
+
* The `application_archetype` manifest trait is a *classification* over a
|
|
5
|
+
* tiered vocabulary. This module resolves a declared value against the union
|
|
6
|
+
* of:
|
|
7
|
+
* - the **method tier** — `unclassified` only, built into sdd-cli (mirrors
|
|
8
|
+
* how the ADR 0173/0176 method-tier scaffold archetypes are built in);
|
|
9
|
+
* - the **reference-architecture / adopter bundle tiers** — seedless
|
|
10
|
+
* vocabulary files (`application-archetypes.yaml`) shipped in consumed
|
|
11
|
+
* bundles and extracted to the local bundle cache
|
|
12
|
+
* (`.sdd/bundle-cache/<bundle>@<version>/`);
|
|
13
|
+
* - an optional **adopter-local** extension file at
|
|
14
|
+
* `.sdd/application-archetypes.yaml` in the SDD repo.
|
|
15
|
+
*
|
|
16
|
+
* This deliberately does NOT reuse the ADR 0176 scaffold registry resolver
|
|
17
|
+
* (`scaffold/registry.ts`): that resolver is seed-oriented and drops any
|
|
18
|
+
* entry with no `seeds[]`, whereas a classification archetype is seedless by
|
|
19
|
+
* design. It DOES share the same bundle-cache distribution substrate — this
|
|
20
|
+
* is a distinct data type over a shared substrate, not a second distribution
|
|
21
|
+
* mechanism (M-ADR 0191 §6).
|
|
22
|
+
*/
|
|
23
|
+
import { readFile, readdir } from "node:fs/promises";
|
|
24
|
+
import { join } from "node:path";
|
|
25
|
+
import { parse as parseYaml } from "yaml";
|
|
26
|
+
import { BUNDLE_CACHE_REL, VOCAB_FILE } from "../../sync/bundle-cache.js";
|
|
27
|
+
/** The sentinel that lives in the method tier — the back-compat default. */
|
|
28
|
+
export const UNCLASSIFIED = "unclassified";
|
|
29
|
+
export { VOCAB_FILE };
|
|
30
|
+
/** Adopter-local extension file, relative to the SDD repo root. */
|
|
31
|
+
export const ADOPTER_VOCAB_REL = join(".sdd", VOCAB_FILE);
|
|
32
|
+
const VALID_PRESENCE = new Set([
|
|
33
|
+
"required",
|
|
34
|
+
"typical",
|
|
35
|
+
"optional",
|
|
36
|
+
"absent-by-shape",
|
|
37
|
+
]);
|
|
38
|
+
/** The built-in method tier (M-ADR 0191 §4.2 — `unclassified` only). */
|
|
39
|
+
const METHOD_TIER = [
|
|
40
|
+
{
|
|
41
|
+
id: UNCLASSIFIED,
|
|
42
|
+
tier: "method",
|
|
43
|
+
shape: "the back-compat default; a product that has not declared a shape",
|
|
44
|
+
source: "method",
|
|
45
|
+
},
|
|
46
|
+
];
|
|
47
|
+
function parseVocabFile(raw, source) {
|
|
48
|
+
let parsed;
|
|
49
|
+
try {
|
|
50
|
+
parsed = parseYaml(raw);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return []; // malformed vocab — skip rather than break the build
|
|
54
|
+
}
|
|
55
|
+
if (parsed === null || typeof parsed !== "object")
|
|
56
|
+
return [];
|
|
57
|
+
if (!Array.isArray(parsed.archetypes))
|
|
58
|
+
return [];
|
|
59
|
+
const tier = typeof parsed.tier === "string" ? parsed.tier : "unknown";
|
|
60
|
+
const out = [];
|
|
61
|
+
for (const a of parsed.archetypes) {
|
|
62
|
+
if (a === null || typeof a !== "object")
|
|
63
|
+
continue;
|
|
64
|
+
const rec = a;
|
|
65
|
+
const id = typeof rec.id === "string" ? rec.id : undefined;
|
|
66
|
+
if (!id)
|
|
67
|
+
continue;
|
|
68
|
+
const expectedBindings = parseExpectedBindings(rec.expected_bindings);
|
|
69
|
+
const conformanceExpectations = parseStringList(rec.conformance);
|
|
70
|
+
out.push({
|
|
71
|
+
id,
|
|
72
|
+
tier,
|
|
73
|
+
source,
|
|
74
|
+
...(typeof rec.layer === "string" && { layer: rec.layer }),
|
|
75
|
+
...(typeof rec.shape === "string" && { shape: rec.shape }),
|
|
76
|
+
...(expectedBindings.length > 0 && { expectedBindings }),
|
|
77
|
+
...(conformanceExpectations.length > 0 && { conformanceExpectations }),
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
return out;
|
|
81
|
+
}
|
|
82
|
+
/** Parse an archetype's `expected_bindings:` list, dropping malformed entries
|
|
83
|
+
* (an unknown presence or a missing pattern is skipped, not fatal). */
|
|
84
|
+
function parseExpectedBindings(raw) {
|
|
85
|
+
if (!Array.isArray(raw))
|
|
86
|
+
return [];
|
|
87
|
+
const out = [];
|
|
88
|
+
for (const e of raw) {
|
|
89
|
+
if (e === null || typeof e !== "object")
|
|
90
|
+
continue;
|
|
91
|
+
const rec = e;
|
|
92
|
+
const pattern = typeof rec.pattern === "string" ? rec.pattern : undefined;
|
|
93
|
+
const presence = typeof rec.presence === "string" ? rec.presence : undefined;
|
|
94
|
+
if (!pattern || !presence || !VALID_PRESENCE.has(presence))
|
|
95
|
+
continue;
|
|
96
|
+
out.push({ pattern, presence: presence });
|
|
97
|
+
}
|
|
98
|
+
return out;
|
|
99
|
+
}
|
|
100
|
+
/** Parse a `conformance:` list of opaque string tags, dropping non-strings. */
|
|
101
|
+
function parseStringList(raw) {
|
|
102
|
+
if (!Array.isArray(raw))
|
|
103
|
+
return [];
|
|
104
|
+
return raw.filter((v) => typeof v === "string" && v.trim() !== "");
|
|
105
|
+
}
|
|
106
|
+
/** readdir(withFileTypes) that yields [] on any error, sorted by name. */
|
|
107
|
+
async function readDirSafe(dir) {
|
|
108
|
+
try {
|
|
109
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
110
|
+
return entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/** Find every application-archetypes.yaml within a cached bundle (root + nested). */
|
|
117
|
+
async function findVocabFiles(dir, depth = 0) {
|
|
118
|
+
const found = [];
|
|
119
|
+
for (const e of await readDirSafe(dir)) {
|
|
120
|
+
if (e.isFile() && e.name === VOCAB_FILE) {
|
|
121
|
+
found.push(join(dir, e.name));
|
|
122
|
+
}
|
|
123
|
+
else if (e.isDirectory() && depth < 4) {
|
|
124
|
+
found.push(...(await findVocabFiles(join(dir, e.name), depth + 1)));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return found;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Discover the active vocabulary for an SDD repo: method tier ∪ every bundle
|
|
131
|
+
* vocab file in the bundle cache ∪ the adopter-local extension file.
|
|
132
|
+
*/
|
|
133
|
+
export async function discoverVocabulary(targetRepo) {
|
|
134
|
+
const terms = [...METHOD_TIER];
|
|
135
|
+
// Bundle tiers (RA + adopter bundles both land in the cache).
|
|
136
|
+
const cacheRoot = join(targetRepo, BUNDLE_CACHE_REL);
|
|
137
|
+
for (const e of await readDirSafe(cacheRoot)) {
|
|
138
|
+
if (!e.isDirectory())
|
|
139
|
+
continue;
|
|
140
|
+
const bundleCacheDir = join(cacheRoot, e.name);
|
|
141
|
+
const at = e.name.lastIndexOf("@");
|
|
142
|
+
const bundleName = at > 0 ? e.name.slice(0, at) : e.name;
|
|
143
|
+
for (const vocabPath of await findVocabFiles(bundleCacheDir)) {
|
|
144
|
+
let raw;
|
|
145
|
+
try {
|
|
146
|
+
raw = await readFile(vocabPath, "utf8");
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
terms.push(...parseVocabFile(raw, bundleName));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// Adopter-local extension.
|
|
155
|
+
try {
|
|
156
|
+
const raw = await readFile(join(targetRepo, ADOPTER_VOCAB_REL), "utf8");
|
|
157
|
+
terms.push(...parseVocabFile(raw, "adopter-local"));
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
// absent — fine
|
|
161
|
+
}
|
|
162
|
+
return { terms, ids: new Set(terms.map((t) => t.id)) };
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Resolve a declared archetype value against a vocabulary. `undefined`
|
|
166
|
+
* defaults to `unclassified` (always resolved). A declared value that is not
|
|
167
|
+
* in the vocabulary is unresolved (the caller emits an error finding).
|
|
168
|
+
*/
|
|
169
|
+
export function resolveArchetype(declared, vocab) {
|
|
170
|
+
const value = declared ?? UNCLASSIFIED;
|
|
171
|
+
return {
|
|
172
|
+
value,
|
|
173
|
+
resolved: vocab.ids.has(value),
|
|
174
|
+
unclassified: value === UNCLASSIFIED,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=vocabulary.js.map
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Default archetype map — android (ADR 0169 §4.2). Method-owned.
|
|
2
|
+
#
|
|
3
|
+
# Android View + Material toolkit. These names are industry-standard (Google/AndroidX/Material)
|
|
4
|
+
# and identical across every Android app — never an adopter's bespoke vocabulary.
|
|
5
|
+
# Resolution order (ADR 0169 §4.3): adopter overlay override -> by_token -> by_suffix_heuristic -> default.
|
|
6
|
+
# Selected per screen by `platform: android` frontmatter.
|
|
7
|
+
|
|
8
|
+
map_version: "1.0.0"
|
|
9
|
+
platform: android
|
|
10
|
+
|
|
11
|
+
# Material/AndroidX vendor prefixes, stripped before token lookup.
|
|
12
|
+
strip_prefixes: [Material, Appcompat, AppCompat, Androidx]
|
|
13
|
+
|
|
14
|
+
by_token:
|
|
15
|
+
ConstraintLayout: stack
|
|
16
|
+
FrameLayout: stack
|
|
17
|
+
LinearLayout: stack
|
|
18
|
+
RelativeLayout: stack
|
|
19
|
+
CoordinatorLayout: scaffold
|
|
20
|
+
NestedScrollView: scroll
|
|
21
|
+
ScrollView: scroll
|
|
22
|
+
RecyclerView: list
|
|
23
|
+
ListView: list
|
|
24
|
+
GridLayout: grid
|
|
25
|
+
GridView: grid
|
|
26
|
+
ViewPager: paged-panes
|
|
27
|
+
ViewPager2: paged-panes
|
|
28
|
+
TabLayout: tab-strip
|
|
29
|
+
BottomNavigationView: tab-bar
|
|
30
|
+
NavigationView: sidebar
|
|
31
|
+
NavigationRailView: sidebar
|
|
32
|
+
Toolbar: app-bar
|
|
33
|
+
AppBarLayout: app-bar
|
|
34
|
+
ActionBar: app-bar
|
|
35
|
+
CollapsingToolbarLayout: app-bar
|
|
36
|
+
Button: button
|
|
37
|
+
MaterialButton: button
|
|
38
|
+
ImageButton: button
|
|
39
|
+
FloatingActionButton: fab
|
|
40
|
+
ExtendedFloatingActionButton: fab
|
|
41
|
+
Chip: tab
|
|
42
|
+
ChipGroup: button-group
|
|
43
|
+
ToggleButton: segmented-control
|
|
44
|
+
TextView: text
|
|
45
|
+
EditText: text
|
|
46
|
+
MaterialTextView: text
|
|
47
|
+
ImageView: image
|
|
48
|
+
ImageFilterView: image
|
|
49
|
+
CardView: card
|
|
50
|
+
MaterialCardView: card
|
|
51
|
+
Snackbar: banner
|
|
52
|
+
BottomSheetDialog: sheet
|
|
53
|
+
AlertDialog: modal
|
|
54
|
+
DialogFragment: modal
|
|
55
|
+
|
|
56
|
+
by_suffix_heuristic:
|
|
57
|
+
Layout: stack
|
|
58
|
+
View: panel
|
|
59
|
+
Button: button
|
|
60
|
+
Bar: app-bar
|
|
61
|
+
Dialog: modal
|
|
62
|
+
Card: card
|
|
63
|
+
Text: text
|
|
64
|
+
Image: image
|
|
65
|
+
|
|
66
|
+
default: panel
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Default archetype map — swiftui (ADR 0169 §4.2). Method-owned.
|
|
2
|
+
#
|
|
3
|
+
# Apple SwiftUI toolkit. Industry-standard Apple names, identical across every SwiftUI app —
|
|
4
|
+
# never an adopter's bespoke vocabulary.
|
|
5
|
+
# Resolution order (ADR 0169 §4.3): adopter overlay override -> by_token -> by_suffix_heuristic -> default.
|
|
6
|
+
# Selected per screen by `platform: swiftui` frontmatter.
|
|
7
|
+
|
|
8
|
+
map_version: "1.0.0"
|
|
9
|
+
platform: swiftui
|
|
10
|
+
|
|
11
|
+
strip_prefixes: []
|
|
12
|
+
|
|
13
|
+
by_token:
|
|
14
|
+
VStack: stack
|
|
15
|
+
HStack: row
|
|
16
|
+
ZStack: stack
|
|
17
|
+
LazyVStack: stack
|
|
18
|
+
LazyHStack: row
|
|
19
|
+
Grid: grid
|
|
20
|
+
LazyVGrid: grid
|
|
21
|
+
LazyHGrid: grid
|
|
22
|
+
ScrollView: scroll
|
|
23
|
+
List: list
|
|
24
|
+
Section: panel
|
|
25
|
+
ForEach: list-item # repeated child marker; rendered as a list-item row
|
|
26
|
+
Form: list
|
|
27
|
+
NavigationView: nav-bar
|
|
28
|
+
NavigationStack: nav-bar
|
|
29
|
+
NavigationSplitView: sidebar
|
|
30
|
+
NavigationLink: button
|
|
31
|
+
TabView: tab-bar
|
|
32
|
+
Button: button
|
|
33
|
+
Menu: list
|
|
34
|
+
Toolbar: app-bar
|
|
35
|
+
ToolbarItem: button
|
|
36
|
+
Text: text
|
|
37
|
+
Label: text
|
|
38
|
+
TextField: text
|
|
39
|
+
SecureField: text
|
|
40
|
+
Image: image
|
|
41
|
+
AsyncImage: image
|
|
42
|
+
Spacer: text # decorative; flagged by screen-styling-primitive-as-structure
|
|
43
|
+
Divider: text # decorative; flagged by screen-styling-primitive-as-structure
|
|
44
|
+
GroupBox: card
|
|
45
|
+
Sheet: sheet
|
|
46
|
+
Alert: modal
|
|
47
|
+
Picker: segmented-control
|
|
48
|
+
|
|
49
|
+
by_suffix_heuristic:
|
|
50
|
+
Stack: stack
|
|
51
|
+
View: panel
|
|
52
|
+
Button: button
|
|
53
|
+
Link: button
|
|
54
|
+
Field: text
|
|
55
|
+
Image: image
|
|
56
|
+
Text: text
|
|
57
|
+
|
|
58
|
+
default: panel
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Default archetype map — uikit (ADR 0169 §4.2). Method-owned.
|
|
2
|
+
#
|
|
3
|
+
# Apple UIKit toolkit. Industry-standard Apple names (UI- prefixed), identical across every
|
|
4
|
+
# UIKit app — never an adopter's bespoke vocabulary.
|
|
5
|
+
# Resolution order (ADR 0169 §4.3): adopter overlay override -> by_token -> by_suffix_heuristic -> default.
|
|
6
|
+
# Selected per screen by `platform: uikit` frontmatter.
|
|
7
|
+
|
|
8
|
+
map_version: "1.0.0"
|
|
9
|
+
platform: uikit
|
|
10
|
+
|
|
11
|
+
# The `UI` vendor prefix is stripped, so the `by_token` keys are unprefixed.
|
|
12
|
+
strip_prefixes: [UI, NS]
|
|
13
|
+
|
|
14
|
+
by_token:
|
|
15
|
+
View: panel
|
|
16
|
+
StackView: stack
|
|
17
|
+
ScrollView: scroll
|
|
18
|
+
TableView: list
|
|
19
|
+
TableViewCell: list-item
|
|
20
|
+
CollectionView: grid
|
|
21
|
+
CollectionViewCell: card
|
|
22
|
+
NavigationBar: nav-bar
|
|
23
|
+
NavigationController: nav-bar
|
|
24
|
+
TabBar: tab-bar
|
|
25
|
+
TabBarController: tab-bar
|
|
26
|
+
Toolbar: app-bar
|
|
27
|
+
BarButtonItem: button
|
|
28
|
+
Button: button
|
|
29
|
+
Label: text
|
|
30
|
+
TextField: text
|
|
31
|
+
TextView: text
|
|
32
|
+
ImageView: image
|
|
33
|
+
PageViewController: paged-panes
|
|
34
|
+
SegmentedControl: segmented-control
|
|
35
|
+
ActivityIndicatorView: text
|
|
36
|
+
AlertController: modal
|
|
37
|
+
Stack: stack
|
|
38
|
+
|
|
39
|
+
by_suffix_heuristic:
|
|
40
|
+
View: panel
|
|
41
|
+
Controller: panel
|
|
42
|
+
Button: button
|
|
43
|
+
Bar: app-bar
|
|
44
|
+
Label: text
|
|
45
|
+
Field: text
|
|
46
|
+
Image: image
|
|
47
|
+
|
|
48
|
+
default: panel
|