@mmerterden/multi-agent-pipeline 20.6.0 → 20.7.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 +18 -0
- package/docs/facts.json +1 -1
- package/manifest.json +71 -71
- package/package.json +1 -1
- package/pipeline/multi-agent-refs/features/design-conformance.md +62 -64
- package/pipeline/scripts/_notices.mjs +11 -0
- package/pipeline/scripts/gen-skills-index.mjs +1 -1
- package/pipeline/skills/.skill-manifest.json +39 -39
- package/pipeline/skills/shared/README.md +1 -1
- package/pipeline/skills/shared/external/agent-introspection-debugging/SKILL.md +1 -0
- package/pipeline/skills/shared/external/android-architecture/SKILL.md +2 -0
- package/pipeline/skills/shared/external/android-performance/SKILL.md +2 -0
- package/pipeline/skills/shared/external/android-security/SKILL.md +2 -0
- package/pipeline/skills/shared/external/backlog/BACKLOG.md +1 -1
- package/pipeline/skills/shared/external/backlog/SKILL.md +56 -33
- package/pipeline/skills/shared/external/ci-cd-pipelines/SKILL.md +1 -0
- package/pipeline/skills/shared/external/compose-components/SKILL.md +2 -0
- package/pipeline/skills/shared/external/compose-navigation/SKILL.md +3 -2
- package/pipeline/skills/shared/external/compose-testing/SKILL.md +2 -0
- package/pipeline/skills/shared/external/council/SKILL.md +1 -0
- package/pipeline/skills/shared/external/css-modern/SKILL.md +1 -0
- package/pipeline/skills/shared/external/database-patterns/SKILL.md +1 -0
- package/pipeline/skills/shared/external/evidence-github/SKILL.md +2 -0
- package/pipeline/skills/shared/external/evidence-registry/SKILL.md +2 -0
- package/pipeline/skills/shared/external/gradle-kotlin-dsl/SKILL.md +2 -0
- package/pipeline/skills/shared/external/html-semantic/SKILL.md +1 -0
- package/pipeline/skills/shared/external/humanizer/SKILL.md +1 -0
- package/pipeline/skills/shared/external/ios-coding-standard/SKILL.md +1 -0
- package/pipeline/skills/shared/external/ios-module-structure/SKILL.md +1 -0
- package/pipeline/skills/shared/external/ios-security/SKILL.md +2 -0
- package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +91 -283
- package/pipeline/skills/shared/external/localization-reuse-map/reference/format-and-output.md +119 -151
- package/pipeline/skills/shared/external/localization-reuse-map/reference/publish-and-snapshot.md +60 -90
- package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +119 -156
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-artifact.py +726 -787
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-spreadsheet.py +253 -288
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-annotations.py +243 -304
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-legacy-labels.py +88 -104
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/publish-confluence.py +181 -235
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-key-shots.py +198 -263
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-overlay.py +461 -466
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-legacy-values.py +145 -151
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-new-values.py +123 -141
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/scan-screen-keys.py +146 -157
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/snapshot-resources.sh +22 -19
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/verify-map.py +156 -140
- package/pipeline/skills/shared/external/nextjs-app-router/SKILL.md +1 -0
- package/pipeline/skills/shared/external/play-store-review/SKILL.md +2 -0
- package/pipeline/skills/shared/external/python-patterns/SKILL.md +1 -0
- package/pipeline/skills/shared/external/react-best-practices/SKILL.md +1 -0
- package/pipeline/skills/shared/external/rest-api-design/SKILL.md +1 -0
- package/pipeline/skills/shared/external/retrofit-networking/SKILL.md +2 -0
- package/pipeline/skills/shared/external/room-database/SKILL.md +2 -0
- package/pipeline/skills/shared/external/search-first/SKILL.md +1 -0
- package/pipeline/skills/shared/external/signal-community/SKILL.md +2 -0
- package/pipeline/skills/shared/external/skill-creator/SKILL.md +80 -41
- package/pipeline/skills/shared/external/skill-creator/audit.md +63 -59
- package/pipeline/skills/shared/external/skill-creator/checklist.md +28 -20
- package/pipeline/skills/shared/external/skill-creator/examples.md +40 -40
- package/pipeline/skills/shared/external/skill-creator/label-check.md +48 -36
- package/pipeline/skills/shared/external/skill-creator/scripts/audit-panel.js +91 -100
- package/pipeline/skills/shared/external/skill-creator/template.md +51 -39
- package/pipeline/skills/shared/external/tailwind-css/SKILL.md +1 -0
- package/pipeline/skills/shared/external/testing-backend/SKILL.md +1 -0
- package/pipeline/skills/shared/external/typescript-patterns/SKILL.md +1 -0
- package/pipeline/skills/shared/external/vue-composition/SKILL.md +1 -0
- package/pipeline/skills/shared/external/web-accessibility/SKILL.md +1 -0
- package/pipeline/skills/shared/external/web-performance/SKILL.md +1 -0
- package/pipeline/skills/shared/external/web-testing/SKILL.md +1 -0
package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md
CHANGED
|
@@ -1,176 +1,139 @@
|
|
|
1
|
-
# Sources
|
|
1
|
+
# Sources and recipes
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
> source. **Retrieval is a cross-reference, not a skim** - that is the whole point of this skill. Every
|
|
5
|
-
> recipe reads files or uses `gh`; no other skill required.
|
|
3
|
+
Where every column of the map comes from, how to trace it, and the traps that produce a confidently wrong row. The whole point of the skill is a cross-reference: each column is checked against at least one other source, never skimmed from one.
|
|
6
4
|
|
|
7
|
-
##
|
|
5
|
+
## Where each input lives
|
|
8
6
|
|
|
9
|
-
| |
|
|
7
|
+
| Input | Source | Access |
|
|
10
8
|
|---|---|---|
|
|
11
|
-
|
|
|
12
|
-
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
filled state (e.g. an item list) still belong to the screen. The folder also has `screenshot.png`,
|
|
37
|
-
`tree.json` (node ids), `tokens.json`. *(Live alternative if a frame isn't exported: Figma REST via a
|
|
38
|
-
host Figma tool - REST works with a token; MCP/live needs Figma Desktop in Dev Mode. The export is
|
|
39
|
-
preferred - single-repo.)*
|
|
40
|
-
2. **Each component's owned keys** - registry, local & instant:
|
|
41
|
-
```bash
|
|
42
|
-
python3 -c "import json;d=json.load(open('Resources/Figma/Components/<NODE-DASH>.json'));print([k['key'] for k in d.get('localizationKeys',[])])"
|
|
43
|
-
```
|
|
44
|
-
These include keys the component renders that **never appear in the screen code** (e.g.
|
|
45
|
-
`ItemListGroup.Title`, `ItemListRow.Delete`). Beware the inverse: a shared
|
|
46
|
-
component (e.g. `HeaderMain`, `BottomActions`) owns many keys the screen does **not** use because the
|
|
47
|
-
screen passes its own title in - confirm against the code.
|
|
48
|
-
3. **What the screen wires** - read the scene/screen file: each `LocalizationStringKey.<Ns>.<leaf>` call
|
|
49
|
-
site is one element; the call site is the label. Screen-specific keys (`SignUpAccountDetails.*`) and
|
|
50
|
-
reused keys (`ModalsCustoms.Ok`) live here.
|
|
51
|
-
|
|
52
|
-
Union the three; tag each key **screen-specific / reusable-component / shared-primitive**. Component-only
|
|
53
|
-
keys: map once where that component is mapped - flag, don't duplicate per screen.
|
|
54
|
-
|
|
55
|
-
**New values:** `python3 scripts/resolve-new-values.py --resources-root <resources-repo> --keys "..." --langs all`.
|
|
56
|
-
|
|
57
|
-
## Legacy side - element → key (TRACE the screen, don't skim the map)
|
|
58
|
-
|
|
59
|
-
1. Start from `specs/<area>/<screen>-spec.md` - its `> Legacy reference (iOS):` blockquote names the entry
|
|
60
|
-
class (VC/VM).
|
|
61
|
-
2. **iOS - follow the chain.** Fetch the VM raw; it builds sections/cells. Form-field labels are **not** in
|
|
62
|
-
the VM - they default inside the **cell presentation models** it instantiates. Follow them:
|
|
63
|
-
```bash
|
|
64
|
-
gh api ".../contents/<path>/<Screen>ViewModel.swift" -H "Accept: application/vnd.github.raw" > /tmp/vm.swift
|
|
65
|
-
grep -nE 'PresentationModel|\.localized' /tmp/vm.swift # find the cell classes it creates
|
|
66
|
-
# then fetch each cell class and grep its keys:
|
|
67
|
-
gh api ".../Forms/TextInputCell/PresentationModels/<Field>TextInputCellPresentationModel.swift" -H "Accept: application/vnd.github.raw" | grep -oE '"[^"]+"\.localized'
|
|
68
|
-
```
|
|
69
|
-
This is how `NewSurnameReq` / `OrderNumber` / `ShippingAddressPlaceHolder` surface - a VM-only grep
|
|
70
|
-
misses them. Keys are the string literal before `.localized`.
|
|
71
|
-
3. **Android.** Layout XML carries labels as `@string/Key`; fragment carries runtime strings as `R.string.`:
|
|
72
|
-
```bash
|
|
73
|
-
gh api ".../res/layout/<screen>.xml" -H "Accept: application/vnd.github.raw" | grep -oE '@string/[A-Za-z0-9_]+' | sort -u
|
|
74
|
-
```
|
|
75
|
-
4. **Web (when the legacy app is a web frontend).** The component wires the key as an i18n call, not a
|
|
76
|
-
string literal - grep the component tree for the call site, not the copy:
|
|
77
|
-
```bash
|
|
78
|
-
gh api ".../src/screens/<Screen>/index.tsx" -H "Accept: application/vnd.github.raw" \
|
|
79
|
-
| grep -oE "(\\\$?t\(['\"]|useTranslation\(['\"])[A-Za-z0-9_.]+" | sort -u
|
|
80
|
-
```
|
|
81
|
-
Covers `t('key')` / `i18n.t('key')` (react-i18next, next-intl) and `$t('key')` (vue-i18n). A template
|
|
82
|
-
literal or variable argument (`` t(`errors.${field}`) ``) is a **dynamic** key exactly like iOS
|
|
83
|
-
`String(format:)` - record the pattern, not a resolvable literal, same rule `scan-screen-keys.py`
|
|
84
|
-
applies. Namespace prefixes (`t('signup.email_label')` inside `useTranslation('signup')`) resolve to
|
|
85
|
-
`signup.email_label` - read the hook/provider call, don't assume the literal alone is the full key.
|
|
86
|
-
|
|
87
|
-
iOS, Android and web usually share the flat key for shared elements; when any platform differs it is
|
|
88
|
-
drift worth a **review** (e.g. `AgentaUserAlreadyAdded` iOS typo vs `AgencyUserAlreadyAdded` Android).
|
|
89
|
-
Record every column present; ` - ` where a platform has none.
|
|
90
|
-
|
|
91
|
-
## Legacy translation values - the in-repo legacy-label snapshot (default), refreshed from the live service
|
|
92
|
-
|
|
93
|
-
Values come from your legacy backend’s label service (the same map the legacy apps render) - but **snapshotted
|
|
94
|
-
in-repo** so the mapper reads them offline, no network round-trip per run (single-repo, like design-export):
|
|
9
|
+
| Figma structure (components, nodes, screenshot) | In-repo design export | Local files |
|
|
10
|
+
| CMS annotations and overlay geometry | Figma REST API (keychain `FIGMA_ACCESS_TOKEN`); Figma MCP in Dev Mode as the rate-limit-free alternative; the export as offline fallback | Network |
|
|
11
|
+
| Screenshot | `screens/<frame>/screenshot.png` in the design export | Local |
|
|
12
|
+
| Specs repo files | `gh api repos/<owner>/<repo>/contents/<path>` with `Accept: application/vnd.github.raw`; no checkout needed | Network |
|
|
13
|
+
| Redesign app and resources repo | Local checkouts | Local |
|
|
14
|
+
| Legacy values | `resources/Localization/Legacy/<lang>.json`, a flat snapshot that is refreshed from the label service | Local, network on refresh |
|
|
15
|
+
|
|
16
|
+
The Android app's committed `strings.xml` is only a key catalog: its values default to the key itself. Real legacy values live in the iOS bundled `language.plist` and, authoritatively, in the label service.
|
|
17
|
+
|
|
18
|
+
## New keys: the three-way union
|
|
19
|
+
|
|
20
|
+
A screen's keys come from three places, and each one misses something the others see:
|
|
21
|
+
|
|
22
|
+
1. **Design export**: read `screens/<frame>/components-used.md` of each state frame the screen has (default, error, loading, filled...).
|
|
23
|
+
2. **Component registry**: each used component's `<node>.json -> localizationKeys`. These are keys the component renders internally, and they are often absent from the screen code.
|
|
24
|
+
3. **Screen code**: on iOS, each call site of `LocalizationStringKey.<Namespace>.<leaf>` counts as one element.
|
|
25
|
+
|
|
26
|
+
Take the union and tag every key: screen-specific, component-owned, or shared (a reusable component or a shared primitive).
|
|
27
|
+
|
|
28
|
+
Registry caveat: a shared component such as `HeaderMain` or `BottomActions` may list keys that this screen never shows, since the screen supplies its own title. Check the code before a registry key goes into the map.
|
|
29
|
+
|
|
30
|
+
### Keys the union cannot see
|
|
31
|
+
|
|
32
|
+
Error, validation and alert strings, and keys assembled at runtime, rarely appear in the design export or the registry. Run the scanner on the implemented screen:
|
|
33
|
+
|
|
95
34
|
```bash
|
|
96
|
-
python3 scripts/
|
|
97
|
-
--snapshot-root <specs-repo>/resources/Localization/Legacy \
|
|
98
|
-
--keys "Continue,EmailAddress,PasswordRule,EmailAlreadyExists"
|
|
35
|
+
python3 scripts/scan-screen-keys.py --out scan.json --screen-path <screen-dir>
|
|
99
36
|
```
|
|
100
|
-
The snapshot is `resources/Localization/Legacy/<lang>.json` - a flat `{key: value}` map per language.
|
|
101
|
-
If your backend namespaces keys under a prefix the app strips (a stored `Mobile-Continue` for the app's
|
|
102
|
-
`Continue`), pass `--prefix Mobile-` and keep the mapping's keys bare; set the mapping's `legacyKeyPrefix`
|
|
103
|
-
so the rendered table still shows the content team the full stored key.
|
|
104
37
|
|
|
105
|
-
|
|
106
|
-
|
|
38
|
+
Every `error` and `dynamic` hit becomes a row. Compare the `static` hits with the union; whatever remains is a key the code wires but the map lacks. Skipping this step drops those strings silently.
|
|
39
|
+
|
|
40
|
+
## New values
|
|
41
|
+
|
|
42
|
+
Values are authored per key in `Suggested/<Key>.json`, in eight languages, and they are shared by all platforms: one value per key, only the key usage differs per platform.
|
|
43
|
+
|
|
107
44
|
```bash
|
|
108
|
-
python3 scripts/
|
|
109
|
-
--
|
|
110
|
-
--headers-file .secrets/label-headers.json \
|
|
111
|
-
--fail-status '9999=gateway blocked the request' --fail-status '50=client version not accepted'
|
|
45
|
+
python3 scripts/resolve-new-values.py --keys "<k1>,<k2>" --resources-root <res> --langs all \
|
|
46
|
+
--catalog <path/to/shipped/Localizable.xcstrings>
|
|
112
47
|
```
|
|
113
|
-
- Keep tokens in `--headers-file` (a JSON object), never in `--header` - argv lands in shell history and CI logs.
|
|
114
|
-
- The response shape is discovered, not assumed: the largest flat `{string: string}` map anywhere in the JSON
|
|
115
|
-
wins, so an enveloped payload works without configuration.
|
|
116
|
-
- `--fail-status CODE=message` turns your backend's in-band error codes into a clear abort instead of an
|
|
117
|
-
empty snapshot. Find these once and record them in your project's own notes.
|
|
118
48
|
|
|
119
|
-
`
|
|
120
|
-
service directly, to verify or refresh. `--plist-root <dir>` is a last-resort offline fallback for a legacy
|
|
121
|
-
iOS app's bundled `<lang>.lproj/language.plist` - typically a stale subset, so a hit there deserves a note.
|
|
122
|
-
Never invent a legacy value - blank + a note beats a guess.
|
|
49
|
+
Always pass `--catalog`. A missing Suggested file means "not in this snapshot", not "unauthored": the snapshot can lag the shipped catalog. With the catalog the value still resolves and the script names the stale snapshot so it can be refreshed with `snapshot-resources.sh`. Only a key absent from both the snapshot and the catalog is truly unauthored. Never open an authoring request for a key the catalog resolved.
|
|
123
50
|
|
|
124
|
-
## CMS
|
|
51
|
+
## CMS copy (Figma annotations)
|
|
125
52
|
|
|
126
|
-
The content team
|
|
127
|
-
(category *Final UX Writing*), `TR: ...` / `EN: ...`. The design text is usually a placeholder ("Giriniz",
|
|
128
|
-
"Lorem ipsum") - the **annotation**, not the design text, is their real value. Figma is edited continuously,
|
|
129
|
-
so fetch it **live**, with fallbacks (priority order):
|
|
53
|
+
The content team writes final copy as Dev Mode annotations in the category "Final UX Writing", usually as `TR: ...` and `EN: ...` lines. The text drawn in the design is often placeholder copy; the annotation is the real value.
|
|
130
54
|
|
|
131
55
|
```bash
|
|
132
|
-
|
|
133
|
-
python3 scripts/fetch-annotations.py --
|
|
134
|
-
|
|
135
|
-
# 2) MCP (no rate limit; the host's Figma plugin dumped the nodes' annotations):
|
|
136
|
-
python3 scripts/fetch-annotations.py --from-mcp _annotations.raw.json --out _annotations.json
|
|
137
|
-
# 3) local fallback (offline; only if design-export tree.json carries annotations):
|
|
138
|
-
python3 scripts/fetch-annotations.py --local design-export/<project>/screens/<slug> --out _annotations.json
|
|
56
|
+
python3 scripts/fetch-annotations.py --out _annotations.json --mapping <map>.json
|
|
57
|
+
python3 scripts/fetch-annotations.py --out _annotations.json --from-mcp <raw-mcp-dump>.json
|
|
58
|
+
python3 scripts/fetch-annotations.py --out _annotations.json --local <design-export>/screens/<frame>
|
|
139
59
|
```
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
60
|
+
|
|
61
|
+
Pair every annotation with its row through `nodeId`: the id is saved in the row as `cmsNodeId`, and the copy goes into `cms: {tr, en}`. An element without an annotation keeps a blank CMS cell. The script lists nodes that have TR but no EN; send those back to the content team.
|
|
62
|
+
|
|
63
|
+
## Legacy keys: trace, do not skim
|
|
64
|
+
|
|
65
|
+
### iOS
|
|
66
|
+
|
|
67
|
+
The spec names the entry point: `specs/<area>/<screen>-spec.md` carries a blockquote `> Legacy reference (iOS):` naming the class. Follow the chain from there:
|
|
68
|
+
|
|
69
|
+
view controller, then view model, then data source, then the cell presentation models, where the `"Key".localized` calls usually are.
|
|
70
|
+
|
|
71
|
+
Form-field labels are normally defaulted inside the cell presentation models (often shared input-cell models), not in the view model. Do not stop at the view model.
|
|
72
|
+
|
|
73
|
+
### Android
|
|
74
|
+
|
|
75
|
+
Labels come from the layout XML as `@string/Key`; strings set at runtime come from the fragment as `R.string.<name>`. For form-field labels the layout is the most reliable place to read them.
|
|
76
|
+
|
|
77
|
+
### Web
|
|
78
|
+
|
|
79
|
+
Call sites are `t('key')` or `i18n.t('key')` (react-i18next, next-intl) and `$t('key')` (vue-i18n), resolved against `locales/<lang>.json`. A template literal or a variable argument is a dynamic key. A namespace from `useTranslation('<ns>')` is prepended to the key, so `t('email_label')` under `useTranslation('signup')` is `signup.email_label`.
|
|
80
|
+
|
|
81
|
+
### Cross-platform drift
|
|
82
|
+
|
|
83
|
+
The same element can carry different keys per platform (`AgentaUserAlreadyAdded` on one, `AgencyUserAlreadyAdded` on the other). That is a `review`. Keep every platform column that has a key and render a dash where a platform has none.
|
|
84
|
+
|
|
85
|
+
## Legacy values
|
|
155
86
|
|
|
156
87
|
```bash
|
|
157
|
-
python3 scripts/
|
|
88
|
+
python3 scripts/resolve-legacy-values.py --keys "Continue,EmailAddress" \
|
|
89
|
+
--snapshot-root resources/Localization/Legacy --langs all --prefix Mobile-
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
- Reading the snapshot needs no network. `fetch-legacy-labels.py` refreshes it and is the only step that goes online.
|
|
93
|
+
- `--live` checks against the service directly; if the live call fails, the script falls back to the snapshot, then to the plists.
|
|
94
|
+
- `--plist-root` reads the bundled `<lang>.lproj/language.plist` as a last resort. It is usually a stale subset, so a hit from it deserves a note.
|
|
95
|
+
- **Backend prefix.** When the backend stores `Mobile-Continue` but the app asks for `Continue`, pass `--prefix Mobile-`, keep the mapping keys bare, and set the mapping's `legacyKeyPrefix` so the table shows the full stored key.
|
|
96
|
+
|
|
97
|
+
## Overlay geometry
|
|
98
|
+
|
|
99
|
+
The overlay does not depend on annotations. With zero annotations every keyed element still gets a gray "awaiting copy" card. The number on a card and on its dot is the index of the matched row in `mapping.rows`, counted from 1, so it agrees with the table rather than with the order on screen.
|
|
100
|
+
|
|
101
|
+
**REST (preferred).** Needs `figmaFileKey` and the real frame node. Take the frame node-id from the design export's `tree.json` root `nodeId`: folder-name suffixes and ids copied from older maps are often wrong. Check that the node text belongs to this screen; background nodes that match no row are dropped automatically.
|
|
102
|
+
|
|
103
|
+
**`--spec` (screenshot-anchored).** Use it when there is no token or when REST geometry is noisy. Measure the box of every visible element on the clean screenshot, give the rows synthetic `cmsNodeId`s (`n1`, `n2`, ...), and write:
|
|
104
|
+
|
|
105
|
+
```json
|
|
106
|
+
{"fileKey": "...", "pages": [{"nodeId": "1024:4096", "image": "screenshot.png",
|
|
107
|
+
"frame": {"w": 375, "h": 812},
|
|
108
|
+
"nodes": [{"id": "n1", "x": 16, "y": 64, "w": 220, "h": 28, "side": "left"}]}]}
|
|
158
109
|
```
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
`
|
|
163
|
-
|
|
164
|
-
|
|
110
|
+
|
|
111
|
+
`image` is either a data URL or a PNG path, resolved from the folder of the spec file. `side` is `left` (default) or `right`: it puts the card on the side where its target sits, which two-column layouts need. Each side is numbered and stacked independently, and right-side connectors are mirrored.
|
|
112
|
+
|
|
113
|
+
The export's `tree.json` stops at component instances and carries no geometry per label, so take only the frame node-id from it.
|
|
114
|
+
|
|
115
|
+
Component-owned keys (list rows, promotion grids, summary cards) are marked on the overlay too, flagged `(owned by <Component>)`, with their keys taken from the registry `localizationKeys`.
|
|
116
|
+
|
|
117
|
+
**Multi-state screens.** Each state gets an overlay of its own, built from its own `--spec`, screenshot and `cmsNodeId`s; a row may keep the same `cmsNodeId` in several states. Stack the results vertically in a single PNG, padded to the widest one with a `#1e1e1e` gutter between them, point the mapping's `overlay` at that PNG, and keep the per-state images next to it.
|
|
118
|
+
|
|
119
|
+
**Isolating a component variant.** Once the library is re-versioned, node-ids exported from it stop resolving: `/components` may return nothing and `/images` returns `null`. Walk the live screen frame instead (`GET /v1/files/<key>/nodes?ids=<frame>&depth=6`), find the `INSTANCE` whose `name` matches, render that id, and take label boxes from its text nodes' `absoluteBoundingBox` multiplied by the render `scale`. Without a token, or when the render fails, cut the variant from a screenshot of one of the states.
|
|
120
|
+
|
|
121
|
+
## Keyshots
|
|
122
|
+
|
|
123
|
+
For each matched row, `render-key-shots.py` saves a crop with the element outlined in red as `keyshot__<NN>__<key>.png` inside `keyshots/`, where NN is the row number in the table, and it writes `<slug>.keyshots.manifest.json`. A row with no match gets a dash, never a guessed crop. Set the mapping's `keyshots` to the manifest file name.
|
|
124
|
+
|
|
125
|
+
Geometry reuses the overlay inputs: REST (visible text only, filtered for ancestor visibility and cumulative opacity) or the same `--spec` files, one per state, repeatable; `image` may be a path such as the committed `screenshot.png`.
|
|
165
126
|
|
|
166
127
|
## Pitfalls
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
- **
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
-
|
|
173
|
-
|
|
174
|
-
-
|
|
175
|
-
-
|
|
176
|
-
|
|
128
|
+
|
|
129
|
+
- **Truncated trees.** On a large specs repo the recursive Git tree is cut off at roughly 43k paths. Search with `gh search code` or fetch through `contents/` calls rather than `git/trees/HEAD?recursive=1`.
|
|
130
|
+
- **Namespaced vs flat keys.** `SignUpAccountDetails.ContinueButton` against legacy `Continue`: reuse is about the value, not the key string.
|
|
131
|
+
- **Hidden form labels.** They sit in shared input-cell models; read them from the Android layout.
|
|
132
|
+
- **Blank is fine.** Empty Old and CMS columns are expected states, not errors.
|
|
133
|
+
- **Scanner skipped.** Error, validation, alert and dynamic keys never show up in the static cross-reference.
|
|
134
|
+
- **Frame origin drift.** A rendered frame PNG may not share the frame's `absoluteBoundingBox` origin. Subtracting the frame x/y from text-node x/y has shifted boxes by tens of pixels, a full row off. Check one crop by eye, and prefer boxes read off the actual screenshot to coordinates mixed from two sources.
|
|
135
|
+
- **Stale snapshot.** Missing from Suggested is not unauthored; pass `--catalog` and refresh the snapshot instead of filing requests.
|
|
136
|
+
- **Value anchoring.** Never anchor a keyshot by value across a whole file: short labels recur on unrelated screens and give a confidently wrong crop, which is worse than a dash. Recover a row only through its own `cmsNodeId`, and leave `characters` out of nodes handed to `render-key-shots.py --spec`, because text present turns value matching back on and can bind an unrelated row.
|
|
137
|
+
- **Confluence auth.** Server / Data Center takes a Bearer PAT (keychain `CONFLUENCE_API_TOKEN`), not the Cloud `user:token` form. Pages are looked up with the `content?title=` query, which reads the database; CQL is avoided because its index lags and leads to duplicate pages.
|
|
138
|
+
- **Secrets.** Never commit a label-service token, Confluence PAT or Figma token into a mapping, a taxonomy file or the docs.
|
|
139
|
+
- **TLS proxies.** Publishing runs through `curl` so a corporate proxy's trust store is honoured (Python `urllib` fails verification there). The token goes in a `chmod 600` config passed with `-K`, never on argv. For the Figma scripts behind such a proxy, export the system roots with `security find-certificate -a -p > roots.pem` and pass `--ca-file roots.pem`.
|