@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.
Files changed (69) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/docs/facts.json +1 -1
  3. package/manifest.json +71 -71
  4. package/package.json +1 -1
  5. package/pipeline/multi-agent-refs/features/design-conformance.md +62 -64
  6. package/pipeline/scripts/_notices.mjs +11 -0
  7. package/pipeline/scripts/gen-skills-index.mjs +1 -1
  8. package/pipeline/skills/.skill-manifest.json +39 -39
  9. package/pipeline/skills/shared/README.md +1 -1
  10. package/pipeline/skills/shared/external/agent-introspection-debugging/SKILL.md +1 -0
  11. package/pipeline/skills/shared/external/android-architecture/SKILL.md +2 -0
  12. package/pipeline/skills/shared/external/android-performance/SKILL.md +2 -0
  13. package/pipeline/skills/shared/external/android-security/SKILL.md +2 -0
  14. package/pipeline/skills/shared/external/backlog/BACKLOG.md +1 -1
  15. package/pipeline/skills/shared/external/backlog/SKILL.md +56 -33
  16. package/pipeline/skills/shared/external/ci-cd-pipelines/SKILL.md +1 -0
  17. package/pipeline/skills/shared/external/compose-components/SKILL.md +2 -0
  18. package/pipeline/skills/shared/external/compose-navigation/SKILL.md +3 -2
  19. package/pipeline/skills/shared/external/compose-testing/SKILL.md +2 -0
  20. package/pipeline/skills/shared/external/council/SKILL.md +1 -0
  21. package/pipeline/skills/shared/external/css-modern/SKILL.md +1 -0
  22. package/pipeline/skills/shared/external/database-patterns/SKILL.md +1 -0
  23. package/pipeline/skills/shared/external/evidence-github/SKILL.md +2 -0
  24. package/pipeline/skills/shared/external/evidence-registry/SKILL.md +2 -0
  25. package/pipeline/skills/shared/external/gradle-kotlin-dsl/SKILL.md +2 -0
  26. package/pipeline/skills/shared/external/html-semantic/SKILL.md +1 -0
  27. package/pipeline/skills/shared/external/humanizer/SKILL.md +1 -0
  28. package/pipeline/skills/shared/external/ios-coding-standard/SKILL.md +1 -0
  29. package/pipeline/skills/shared/external/ios-module-structure/SKILL.md +1 -0
  30. package/pipeline/skills/shared/external/ios-security/SKILL.md +2 -0
  31. package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +91 -283
  32. package/pipeline/skills/shared/external/localization-reuse-map/reference/format-and-output.md +119 -151
  33. package/pipeline/skills/shared/external/localization-reuse-map/reference/publish-and-snapshot.md +60 -90
  34. package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +119 -156
  35. package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-artifact.py +726 -787
  36. package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-spreadsheet.py +253 -288
  37. package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-annotations.py +243 -304
  38. package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-legacy-labels.py +88 -104
  39. package/pipeline/skills/shared/external/localization-reuse-map/scripts/publish-confluence.py +181 -235
  40. package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-key-shots.py +198 -263
  41. package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-overlay.py +461 -466
  42. package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-legacy-values.py +145 -151
  43. package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-new-values.py +123 -141
  44. package/pipeline/skills/shared/external/localization-reuse-map/scripts/scan-screen-keys.py +146 -157
  45. package/pipeline/skills/shared/external/localization-reuse-map/scripts/snapshot-resources.sh +22 -19
  46. package/pipeline/skills/shared/external/localization-reuse-map/scripts/verify-map.py +156 -140
  47. package/pipeline/skills/shared/external/nextjs-app-router/SKILL.md +1 -0
  48. package/pipeline/skills/shared/external/play-store-review/SKILL.md +2 -0
  49. package/pipeline/skills/shared/external/python-patterns/SKILL.md +1 -0
  50. package/pipeline/skills/shared/external/react-best-practices/SKILL.md +1 -0
  51. package/pipeline/skills/shared/external/rest-api-design/SKILL.md +1 -0
  52. package/pipeline/skills/shared/external/retrofit-networking/SKILL.md +2 -0
  53. package/pipeline/skills/shared/external/room-database/SKILL.md +2 -0
  54. package/pipeline/skills/shared/external/search-first/SKILL.md +1 -0
  55. package/pipeline/skills/shared/external/signal-community/SKILL.md +2 -0
  56. package/pipeline/skills/shared/external/skill-creator/SKILL.md +80 -41
  57. package/pipeline/skills/shared/external/skill-creator/audit.md +63 -59
  58. package/pipeline/skills/shared/external/skill-creator/checklist.md +28 -20
  59. package/pipeline/skills/shared/external/skill-creator/examples.md +40 -40
  60. package/pipeline/skills/shared/external/skill-creator/label-check.md +48 -36
  61. package/pipeline/skills/shared/external/skill-creator/scripts/audit-panel.js +91 -100
  62. package/pipeline/skills/shared/external/skill-creator/template.md +51 -39
  63. package/pipeline/skills/shared/external/tailwind-css/SKILL.md +1 -0
  64. package/pipeline/skills/shared/external/testing-backend/SKILL.md +1 -0
  65. package/pipeline/skills/shared/external/typescript-patterns/SKILL.md +1 -0
  66. package/pipeline/skills/shared/external/vue-composition/SKILL.md +1 -0
  67. package/pipeline/skills/shared/external/web-accessibility/SKILL.md +1 -0
  68. package/pipeline/skills/shared/external/web-performance/SKILL.md +1 -0
  69. package/pipeline/skills/shared/external/web-testing/SKILL.md +1 -0
@@ -1,156 +1,124 @@
1
- # Verdict taxonomy, mapping schema & output
1
+ # Mapping format and outputs
2
2
 
3
- > Bundled detail for [../SKILL.md](../SKILL.md). How to classify each row, the JSON the build script
4
- > consumes, and the artifacts it produces. Input shape: [../example-mapping.json](../example-mapping.json).
3
+ The mapping JSON is the single source every renderer reads. The scripts add nothing of their own: what is not in the mapping does not appear in the table, the spreadsheet or the page.
5
4
 
6
- ## Verdict taxonomy (recommendation; content team confirms)
5
+ ## Mapping schema
7
6
 
8
- | Verdict | When | What it tells the content team |
7
+ Top level:
8
+
9
+ | Key | Required | Meaning |
9
10
  |---|---|---|
10
- | ✅ **reuse** | Same function **and** same/equivalent value (or a clean shared key on both legacy platforms) | Adopt the legacy value/key - translators don't re-translate |
11
- | 🔶 **review** | Same function but value drift, wording change, cross-platform key drift, or thin legacy language coverage | Same element exists - you decide whether to reuse the value or take the new copy |
12
- | 🆕 **new** | No legacy counterpart (new functionality / new business rule) | Mint a new key + value |
13
-
14
- Edge calls to catch in the note: a `tr` value equal to `en` (untranslated); legacy present in only some
15
- languages (e.g. en/tr only); a key owned by a reusable component (flag, don't double-map); a legacy key on
16
- only one platform; **a CMS (content-team) value that differs from the new design value** (the renderer marks
17
- it ⚠ - call it out so the content team knows their final copy diverged from the mock).
18
-
19
- ## Mapping JSON schema (input to `scripts/build-artifact.py`)
20
-
21
- ```jsonc
22
- {
23
- "screen": "Sign Up - Account Details", // required
24
- "platforms": ["ios", "android", "web"],
25
- "figmaNodes": ["1024:4096", "1024:4112"],
26
- "figmaFileKey": "AbCdEf123", // optional; enables live annotation fetch / overlay
27
- "screenshot": "screenshot.png", // optional; rendered in section Screen
28
- "overlay": "<slug>.overlay.png", // optional; key↔UI image (render-overlay.py) → section key map
29
- "keyshots": "<slug>.keyshots.manifest.json", // optional; per-key screenpiece manifest (render-key-shots.py)
30
- // → fills the Summary "Screenshot" cells
31
- "rows": [ // required
32
- {
33
- "element": "Continue button", // the UI element label
34
- "newKey": "SignUpAccountDetails.ContinueButton", // "(owned by ...)" if not screen-owned
35
- "new": {"en":"Continue","tr":"Devam Et","ar":"...","de":"...","es":"...","fr":"...","it":"...","ru":"..."},
36
- "legacyKeyIOS": "Continue", // "" → renders " - "
37
- "legacyKeyAndroid": "Continue",
38
- "legacyKeyWeb": "Continue", // "" or omitted → renders " - "; only when the legacy app has a web frontend
39
- "legacy": {"en":"Continue","tr":"Devam","ar":"...", ...}, // 8 langs; "" where service-only
40
- "cms": {"tr":"Devam Et","en":"Continue"}, // optional; content team's ACTUAL copy (Figma annotation)
41
- "cmsNodeId": "1024:4150", // optional; the Figma node the annotation sits on
42
- "cmsSource": "figma-rest", // optional; figma-rest | figma-mcp | local | none
43
- "verdict": "review", // reuse | review | new
44
- "note": "Legacy tr 'Devam' vs new 'Devam Et'."
45
- }
46
- ]
47
- }
48
- ```
49
- Fill `new` with `resolve-new-values.py --langs all`, `legacy` with `resolve-legacy-values.py --langs all`,
50
- and `cms` from `fetch-annotations.py` (the content team's Final UX Writing TR/EN; see
51
- [sources-and-recipes.md](sources-and-recipes.md)). The renderer shows **en/tr** old/new **+ CMS tr/en** in the
52
- summary, and **all 8** old/new **plus every CMS language present** in the details - so keep the `new`/`legacy`
53
- maps fully 8-keyed; `cms` is whatever the content team annotated (usually tr+en).
54
-
55
- **CMS columns + drift.** `cms` is the content team's *actual* final copy and is shown additively - it does
56
- **not** overwrite `new`. When a `cms` value differs from the `new` (design/resources) value for the same
57
- language, the renderer marks the cell **⚠** and counts it as a CMS difference in the header.
58
- Blank `cms` is expected (no annotation yet) - never invent one.
59
-
60
- **Author `element` and `note` in the content team's own language** - pick it once (`--ui-lang` sets the
61
- template chrome to match) and stay consistent. These two author-written fields are the only free text in the
62
- doc, so a mixed-language run reads as half-translated. `newKey` / legacy keys and the translation values stay
63
- verbatim.
64
-
65
- ## Output - artifacts (one mapping)
66
-
67
- `build-artifact.py <mapping>.json --out <dir>` always writes these three:
68
- - **`<slug>.md`** - Markdown mirror (PR / quick read).
69
- - **`<slug>.confluence.xml`** - Confluence **storage format**: section Screen (`<ac:image>`), section key map (the overlay
70
- `<ac:image>` when `overlay` is set), section Summary (Old en/tr · New en/tr · **CMS tr/en** + verdict, `status`
71
- macros, `info`-macro legend + counts + drift note, and - when `keyshots` is set - a per-row
72
- **Screenshot** cell embedding that key's red-box screenpiece by attachment filename), section Details
73
- (per-element tables: all 8 langs Old/New + CMS). This is exactly the REST API's `body.storage.value`.
74
- - **`<slug>.preview.html`** - standalone browser preview (open locally before upload).
75
-
76
- Two more are **opt-in**, for handing the map to the content team as a file (not a Confluence page):
77
- - **`<slug>.docx`** (`--docx`) - a Word document with the same section Screen / section Summary / section Details layout
78
- (landscape, verdict-shaded cells, embedded screenshot, RTL on the Arabic rows). Generated with the
79
- **Python stdlib only** - a `.docx` is a ZIP of OOXML, so there is no third-party dependency and no network.
80
- - **`<slug>.pdf`** (`--pdf`) - a read-only share copy. Multilingual (Arabic + Cyrillic + Latin) PDF needs real
81
- font shaping, so it is **not** hand-rolled: the script drives whatever renderer the machine has, in order
82
- of table fidelity - LibreOffice `soffice` (docx→pdf) → Chrome/Chromium headless (html→pdf) → `wkhtmltopdf`.
83
- If none is installed the PDF is **skipped** with a note pointing at the `.docx` / `.html` (still no pip, no
84
- network). `--all` writes every format.
85
-
86
- Layout = **summary (en/tr at a glance) + details (all 8)** - the "best of both worlds": devs scan the
87
- summary, the content team works the per-element language tables. The `.docx`/`.pdf` mirror the same two levels.
88
-
89
- ## Per-key screenpieces (keyshots) - the "Screenshot" column
90
-
91
- `scripts/render-key-shots.py --mapping <map>.json --out <dir>` renders, for **every matched row**, a cropped
92
- band of the screen with a **red box around exactly that element** (ported from the web skill's
93
- `render-key-shots.mjs`) - so the table shows the content team *where each key lives on screen* without
94
- hunting through the full overlay. Files land in `<out>/keyshots/keyshot__<NN>__<key>.png`, where **NN is
95
- the row's 1-based position in `mapping.rows`** - the same number the Summary table and the overlay cards
96
- use, so shot ↔ card ↔ row always agree. A manifest (`<slug>.keyshots.manifest.json`) lists
97
- `shots` (row → file) and `missing` (rows with no visible matched node - their cells render " - ", never a
98
- guessed crop).
99
-
100
- Geometry comes from the same two modes as the overlay: **REST** (walks the frame's node tree with
101
- **ancestor-visibility + cumulative-opacity** filtering, so a hidden variant's stale bbox never lands the
102
- red box on the wrong content) or **`--spec`** (repeatable - one per screen state; the overlay-compatible
103
- spec shape, whose `image` may be a **data URL or a file path** relative to the spec file). Rows are matched
104
- to nodes with the overlay's matcher (`cmsNodeId` first, else normalized value), so the three renderers can
105
- never disagree about which node a key is. Set the mapping's top-level `"keyshots"` to the manifest filename
106
- and `build-artifact.py` fills the **Screenshot** column in all four formats (Confluence XML by
107
- attachment filename, `.md`/`.preview.html` by relative path, `.docx` embedded). No headless Chrome →
108
- per-shot `.html` fallbacks (marked in the manifest; not embeddable).
109
-
110
- ## CMS import spreadsheet (Excel attachment)
111
-
112
- `scripts/build-spreadsheet.py <mapping>.json --out <dir>` writes **`<slug>.localization.xlsx`** - the flat
113
- sheet the content team imports into the CMS, attached to the Confluence page (and, being a spreadsheet, also
114
- rendered inline via a `view-file` macro). It is a **real `.xlsx`**: an OOXML zip assembled with the Python
115
- **stdlib only** (`zipfile` + inline-string XML) - no pip, no LibreOffice, no network, exactly like the
116
- `.docx` path. `--csv` writes a UTF-8 (BOM) CSV alongside; `--csv-only` skips the xlsx when a CSV is all that's
117
- wanted.
118
-
119
- **One row per mapping row**, in this fixed column order (matches the content team's sheet):
120
-
121
- | Column | Source | Notes |
11
+ | `screen` | yes | Screen name; the file slug is derived from it |
12
+ | `platforms` | no | e.g. `["ios", "android", "web"]` |
13
+ | `figmaNodes` | no | Array of frame node-ids |
14
+ | `figmaFileKey` | no | Enables the live annotation fetch and the REST overlay |
15
+ | `screenshot` | no | Screen image, relative to the mapping |
16
+ | `overlay` | no | Overlay PNG name |
17
+ | `keyshots` | no | Keyshots manifest name |
18
+ | `legacyKeyPrefix` | no | Prepended to legacy keys for display only |
19
+ | `rows` | yes | One object per UI element |
20
+
21
+ Row:
22
+
23
+ | Key | Meaning |
24
+ |---|---|
25
+ | `element` | Human label for the UI element, in the `--ui-lang` language |
26
+ | `newKey` | Redesign key; may carry `(owned by <Component>)` |
27
+ | `new` | Eight-language map: `en`, `tr`, `ar`, `de`, `es`, `fr`, `it`, `ru` |
28
+ | `legacyKeyIOS`, `legacyKeyAndroid`, `legacyKeyWeb` | Legacy key per platform. Empty or omitted renders a dash. Web only when the legacy app has a web frontend. A value in parentheses such as `(nav title)` is a marker and is shown as is |
29
+ | `legacy` | Eight-language map; `""` where the value is only served by the backend and was not fetched |
30
+ | `cms` | Optional; usually `tr` and `en` from the Figma annotation |
31
+ | `cmsNodeId` | Optional; the annotated (or synthetic spec) node-id |
32
+ | `cmsSource` | Optional: `figma-rest`, `figma-mcp`, `local` or `none` |
33
+ | `verdict` | `reuse`, `review` or `new` |
34
+ | `note` | Reasoning, edge-case flags, `(owned by ...)`, "dynamic - enumerate at runtime" |
35
+ | `propertyGroup`, `propertyModule` | Optional spreadsheet-only overrides |
36
+
37
+ Keep `new` and `legacy` fully eight-keyed. The summary table shows old and new `en`/`tr` plus CMS `tr`/`en`; the details section shows all eight languages plus every CMS language present.
38
+
39
+ ### The example
40
+
41
+ [`../example-mapping.json`](../example-mapping.json) maps a neutral "Sign Up - Account Details" screen in nine rows, with `legacyKeyPrefix: "Mobile-"`:
42
+
43
+ | # | Case | Verdict |
122
44
  |---|---|---|
123
- | **Channel** | constant `Mobile` | the delivery channel these strings belong to; override with `--channel` |
124
- | **Property Group** | deduced from the key namespace | default buckets `Core` / `Domains` / `Errors` / `Lookups`; rename them with `--taxonomy`, or override per row with `propertyGroup` |
125
- | **Property Module** | deduced from the key namespace | `Fields` / `Validation` / `Common` / `<domain>`; row override `propertyModule` |
126
- | **Key** | `row.newKey` | our new key, verbatim; a trailing `(owned by ...)` component tag is stripped |
127
- | **EN / TR / AR Value** | `row.new.en` / `.tr` / `.ar` | our **Suggested** values (resolve-new-values.py) |
128
- | **Anotation EN / TR** | `row.cms.en` / `.tr` | the content team's **CMS Figma annotations** (fetch-annotations.py); blank when unannotated |
129
-
130
- **Group/Module deduction** is a best-effort heuristic on the key: `error*`→`Errors`, `lookup`→`Lookups`,
131
- `validation`→`Core/Validation`, `field|placeholder|hint|label`→`Core/Fields`, a recognized feature word
132
- (auth/login, account/profile, checkout/payment, search...)→`Domains/<domain>`, else `Core/Common`. Every bucket
133
- name and every domain word list comes from `--taxonomy <file>` (merged over those defaults), because no two
134
- CMS instances name their buckets alike. When the agent knows better still, set
135
- `propertyGroup`/`propertyModule` on the row and the script uses those verbatim. **Values/annotations are never fabricated** - blank cells mean unsourced, same rule as
136
- the doc (`new` for values, `cms` for annotations).
137
-
138
- ## Publishing - live, idempotent
139
-
140
- Automated by **`scripts/publish-confluence.py`** (full recipe in
141
- [publish-and-snapshot.md](publish-and-snapshot.md)). It uploads the `<slug>.confluence.xml` body to a
142
- Confluence **Server/DC** instance with a **Bearer PAT** (keychain `CONFLUENCE_API_TOKEN`), and is **idempotent
143
- by title** - re-running on a screen **updates the same page in place** (version-bump), never duplicating it.
144
- It attaches `screenshot.png` + the overlay PNG + (via `--keyshots`) every per-key screenpiece PNG (the
145
- storage `<ac:image>` tags reference them by filename).
146
- The target base URL / space / parent come from `--base-url` / `--space` / `--parent` or the matching
147
- `CONFLUENCE_*` environment variables, and are **asked from the user** when neither is set.
148
-
149
- ```bash
150
- python3 publish-confluence.py --xml <slug>.confluence.xml --screen "<name>" \
151
- --screenshot screenshot.png --overlay <slug>.overlay.png \
152
- --keyshots <slug>.keyshots.manifest.json # [--space <SPACE> --parent <id>] [--title ...]
153
- ```
154
- `build-artifact.py ... --print-upload` prints this exact command (plus the equivalent raw Bearer `curl`).
155
- Manual fallback: paste `<slug>.confluence.xml` into the page's storage editor and attach the two images by
156
- filename.
45
+ | 1 | Nav title with no iOS legacy key (`(nav title)` marker) | review |
46
+ | 2 | One shared key, identical on all three platforms | reuse |
47
+ | 3 | Placeholder with no legacy counterpart | new |
48
+ | 4 | Component-owned `PasswordField.StrengthHint` with cross-platform key drift | review |
49
+ | 5 | Duplicate-email error found only by the scanner; the Android key has a typo | review |
50
+ | 6 | CTA whose web key is namespaced (`signup.continue_button`) | review |
51
+ | 7 | `tr` equal to `en` in the redesign source | reuse, flagged untranslated |
52
+ | 8 | CMS-only `tr` that differs from `new` | new |
53
+ | 9 | Dynamic `Error.<field>Required` | review, "enumerate at runtime" |
54
+
55
+ ## Rendered artifacts
56
+
57
+ `build-artifact.py <map>.json --out <dir>` always writes three files, and more on request:
58
+
59
+ | File | Contents |
60
+ |---|---|
61
+ | `<slug>.md` | Title, intro, platforms and Figma nodes, screenshot, optional key-map image, summary with counts, drift note, pipe table (pipes escaped, newlines flattened, keyshot as a relative image), per-row detail tables |
62
+ | `<slug>.confluence.xml` | Storage format, the value of `body.storage.value`: header paragraph, an `info` macro with the legend (status macros), counts, CMS line and drift note; a Screen section (`<ac:image ac:height="500">`); a key-map section (`<ac:image ac:width="900">`); the summary table with keyshots (`<ac:image ac:width="240">`) referenced by attachment name, keys in `<code>`, status macros; per-row details with an eight-language Old / New / CMS table |
63
+ | `<slug>.preview.html` | Standalone page with sections 1 Screen, 2 Summary, 3 Details and coloured verdict badges. Absolute image paths become `file://` URLs; bare names stay relative |
64
+ | `<slug>.docx` (`--docx`) | Word document built with the standard library: landscape A4, screenshot, overlay and keyshot thumbnails, verdict-shaded cells, a pink cell for drifting CMS TR, right-to-left Arabic rows |
65
+ | `<slug>.pdf` (`--pdf`) | LibreOffice from the docx, then headless Chrome from the preview, then `wkhtmltopdf`. With none of them installed the PDF is skipped with a note naming the file to share instead |
66
+
67
+ `--all` means `--docx --pdf`. Summary columns in order: `#`, UI element, Screenshot, New key, Legacy iOS, Legacy Android, Legacy Web, Old en, Old tr, New en, New tr, CMS TR, CMS EN, Verdict, Note. Detail columns: Lang, Old (legacy), New (redesign), CMS. Verdict colours on Confluence: reuse Green, review Yellow, new Blue, anything else Grey.
68
+
69
+ A CMS value counts as drifting when both it and `new` are non-empty and they still differ after Unicode NFC normalisation, whitespace collapsing, stripping a trailing `.`, `:` or ellipsis, and lowercasing. Drifting cells carry a warning sign.
70
+
71
+ The `--ui-lang` switch (`en` default, `tr`) changes headings, legend, labels and column names; keys and values are never translated.
72
+
73
+ ## Overlay and keyshots
74
+
75
+ - `render-overlay.py` writes `<slug>.overlay.png` (or `<slug>.overlay.html` when no Chrome, Chromium or Edge is installed) and `<slug>.overlay.manifest.json` with `screen`, `file`, `fileKey`, `nodeId`, `cardCount`, `annotatedCount`, `width`, `height`, `scale`. Pink cards carry CMS copy; gray cards show the on-screen text and await copy.
76
+ - `render-key-shots.py` writes `keyshots/keyshot__<NN>__<key>.png` (or `.html` crops with `"fallback": "html"`) and `<slug>.keyshots.manifest.json` with `screen`, `slug`, `pad`, `scale`, `dir`, `shots[]` (`row`, `key`, `element`, `file`, `pageNode`, `cropW`, `cropH`) and `missing[]` (`row`, `key`, `reason`).
77
+
78
+ ## CMS import spreadsheet
79
+
80
+ `build-spreadsheet.py <map>.json --out <dir>` writes `<slug>.localization.xlsx`, a real Excel workbook built with the standard library (sheet `Localization`, styled and frozen header row, autofilter). `--csv` adds a UTF-8 CSV with BOM; `--csv-only` skips the xlsx. One row per key, attached to the Confluence page at publish time.
81
+
82
+ Nine fixed columns, spelled exactly like this:
83
+
84
+ `Channel`, `Property Group`, `Property Module`, `Key`, `EN Value`, `TR Value`, `AR Value`, `Anotation EN`, `Anotation TR`
85
+
86
+ - `Channel` comes from `--channel` (default `Mobile`).
87
+ - `Key` is `newKey` with any `(owned by ...)` suffix removed.
88
+ - Values come from `new.en/tr/ar`, annotations from `cms.en/tr`; missing means blank.
89
+ - Property Group and Module: row overrides win (each fills its own half). Otherwise the key decides, in order: contains `error` gives Errors; `lookup` gives Lookups; `validation`, `invalid` or a `.valid` ending gives Core / Validation; `field`, `placeholder`, `hint` or `label` gives Core / Fields; a first namespace segment or any word matching a domain list gives the domain group (`Domains`) and the domain name; anything else is Core / Common.
90
+ - Default domains: auth (auth, login, signin, sign-in, register, signup, otp, password), account (account, profile, settings, preferences), checkout (checkout, cart, payment, billing, order), search (search, filter, results). A `--taxonomy` JSON object is merged one level deep over these defaults.
91
+
92
+ ## Fidelity gate
93
+
94
+ `verify-map.py --mapping <map>.json --screen-path <dir> [--resources-root <res>] [--out report.json]` is count reconciliation as a script.
95
+
96
+ | Check | Hard? |
97
+ |---|---|
98
+ | Each `newKey` is wired in the screen code (exact match), or exempt as component-owned or dynamic. A key whose last segment only appears as a word is `weak`; neither is `unverified` | unverified: hard |
99
+ | Keys the code wires that no row maps (`codeKeysNotInMap`) | hard |
100
+ | `reuse` / `review` row without any legacy `en` or `tr` value | hard |
101
+ | Non-`new` row with an empty `new.tr` | soft |
102
+ | `new.tr` equal to `new.en` (untranslated) | soft |
103
+ | With `--resources-root`: no `Suggested/<Key>.json` in the snapshot or the source layout | soft |
104
+
105
+ A row is dynamic-exempt when its key starts with `(dynamic`, contains a `<placeholder>`, its element mentions dynamic, or its note says "enumerate at runtime". Exit 2 when any hard issue exists; publish only after fixing them (drop or fix UNVERIFIED keys, add rows for wired-but-unmapped keys) or after a conscious, stated waiver.
106
+
107
+ ## Script reference
108
+
109
+ | Script | Key flags |
110
+ |---|---|
111
+ | `scan-screen-keys.py` | `--screen-path` (required), `--out`, `--category all/error/dynamic/static` |
112
+ | `resolve-new-values.py` | `--keys`, `--keys-file` (`-` for stdin), `--resources-root` or `--suggested-dir`, `--domain localization/accessibility`, `--catalog`, `--report-source`, `--langs` (default `en,tr`, or `all`) |
113
+ | `resolve-legacy-values.py` | `--keys` (required), `--langs`, `--snapshot-root`, `--live`, `--endpoint`, `--header K=V`, `--headers-file`, `--env`, `--status-field`, `--fail-status CODE=message`, `--timeout`, `--plist-root`, `--prefix` |
114
+ | `fetch-legacy-labels.py` | `--out`, `--endpoint` (both required), `--header`, `--headers-file`, `--langs` (default `all`), `--env`, `--status-field`, `--fail-status`, `--timeout` |
115
+ | `fetch-annotations.py` | `--file`, `--nodes`, `--mapping`, `--from-mcp`, `--local`, `--source auto/rest/mcp/local`, `--token`, `--ca-file`, `--out` |
116
+ | `render-overlay.py` | `--mapping` (required), `--spec`, `--file`, `--nodes`, `--scale`, `--token`, `--ca-file`, `--out`, `--slug` |
117
+ | `render-key-shots.py` | `--mapping` (required), `--spec` (repeatable), `--file`, `--nodes`, `--pad` (default 150), `--scale`, `--token`, `--ca-file`, `--out`, `--slug` |
118
+ | `build-artifact.py` | `mapping` (or `-`), `--out`, `--slug`, `--ui-lang en/tr`, `--docx`, `--pdf`, `--all`, `--print-upload` |
119
+ | `build-spreadsheet.py` | `mapping` (or `-`), `--out`, `--slug`, `--channel`, `--taxonomy`, `--csv`, `--csv-only` |
120
+ | `verify-map.py` | `--mapping`, `--screen-path` (both required), `--resources-root`, `--out` |
121
+ | `publish-confluence.py` | see [publish-and-snapshot](publish-and-snapshot.md) |
122
+ | `snapshot-resources.sh` | `<resources-root> <specs-root>` |
123
+
124
+ `all` languages means `en, tr, ar, de, es, fr, it, ru` in every script. Every script derives the same slug from `screen`: lowercase, each run of non-letter, non-digit characters becomes one `-`, leading and trailing dashes trimmed, `screen` when nothing is left. `--slug` overrides it on any script. Figma tokens resolve from `--token`, then `FIGMA_ACCESS_TOKEN`, then `FIGMA_TOKEN`, then the macOS keychain item `FIGMA_ACCESS_TOKEN`; prefer the environment or keychain, since argv is visible to other processes.
@@ -1,108 +1,78 @@
1
- # Publish to <specs-repo> & the in-repo resource snapshot
1
+ # Publishing and the resources snapshot
2
2
 
3
- > Bundled detail for [../SKILL.md](../SKILL.md). How the generated artifact lands in `<specs-repo>`, and
4
- > how the redesign resources get snapshotted there so the whole flow is **single-repo** (like `design-export`).
3
+ ## One repo to read from
5
4
 
6
- ## Single-repo model - why snapshot into <specs-repo>
5
+ The component registry and the Suggested translations are kept in their own resources repo. So that every lookup happens in a single place, a snapshot of both is copied into the specs repo under `resources/`, beside the design export:
7
6
 
8
- The skill reads four sources. Three already live in `<specs-repo>` (`design-export/`, legacy iOS/Android,
9
- the live label service over the network). The fourth - the redesign **component registry** and **Suggested
10
- translations** - lives in `<resources-repo>`. Snapshotting those two into `<specs-repo>`
11
- (exactly as `design-export/` is a manual/CI snapshot of Figma) removes the cross-repo round-trip: every
12
- read is one repo.
13
-
14
- **Layout** (top-level, mirroring `design-export`'s convention):
15
7
  ```
16
8
  <specs-repo>/
17
- ├── design-export/ # existing (Figma snapshot)
18
- ├── resources/ # NEW snapshot - all data the mapper needs, in-repo
19
- │ ├── Figma/Components/<node>.json # component registry (localizationKeys, componentName)
20
- │ ├── Localization/Suggested/<Key>.json # NEW translation values (8 langs)
21
- │ └── Localization/Legacy/<lang>.json # LEGACY values - label snapshot (flat key→value, one file per language)
22
- └── localizations/ # NEW - the maps this skill publishes
23
- └── <screen-slug>/
24
- ├── <screen-slug>.md # the reuse map
25
- ├── <screen-slug>.confluence.xml
26
- ├── <screen-slug>.overlay.png # key↔UI overlay (when produced)
27
- ├── <screen-slug>.keyshots.manifest.json # per-key screenpiece index (when produced)
28
- ├── keyshots/ # per-key red-box screenpieces (render-key-shots.py)
29
- │ └── keyshot__<NN>__<key>.png
30
- └── screenshot.png
9
+ resources/
10
+ Figma/Components/<node>.json
11
+ Localization/Suggested/<Key>.json
12
+ Localization/Legacy/<lang>.json
13
+ localizations/<screen-slug>/
14
+ <slug>.md
15
+ <slug>.confluence.xml
16
+ <slug>.overlay.png
17
+ <slug>.keyshots.manifest.json
18
+ <slug>.localization.xlsx
19
+ screenshot.png
20
+ keyshots/ (one keyshot__<NN>__<key>.png per row)
31
21
  ```
32
22
 
33
- **Refresh** - two steps, both "like a `design-export` update" (run by CI or manually):
23
+ ### Refreshing it
24
+
25
+ The refresh has two parts. The first runs locally, the second needs the network:
26
+
34
27
  ```bash
35
- # 1. registry + new (Suggested) values - local copy from <resources-repo> (no network)
36
28
  scripts/snapshot-resources.sh <resources-repo-root> <specs-repo-root>
37
- # 2. legacy values - the ONLY network step: fetch the legacy labels for all 8 languages
38
- scripts/fetch-legacy-labels.py --out <specs-repo>/resources/Localization/Legacy --langs all
29
+ python3 scripts/fetch-legacy-labels.py --langs all \
30
+ --out <specs-repo-root>/resources/Localization/Legacy \
31
+ --endpoint '<label-url-with-{lang}>' --headers-file <headers.json>
39
32
  ```
40
- With the snapshot in place every read is in-repo - **no live label round-trip per run**:
41
- - new values: `resolve-new-values.py --resources-root <specs-repo>/resources` (Suggested lives there)
42
- - legacy values: `resolve-legacy-values.py --snapshot-root <specs-repo>/resources/Localization/Legacy`
43
- (offline; `--live` only to verify/refresh, `--plist-root` as last resort).
44
33
 
45
- ## Publishing a screen's map (the skill's last step)
34
+ `snapshot-resources.sh` copies `Resources/Figma/Components` and `Resources/Localization/LocalizationStrings/Sources/Suggested` with `rsync -a --delete`; a key or component deleted upstream therefore drops out of the snapshot as well. If either source folder is missing, the script stops with `missing source dir: <dir>`. `fetch-legacy-labels.py` saves a flat `<lang>.json` for every language, sorted by key, and leaves each key exactly as the service stores it, prefix and all. Credential headers belong in `--headers-file` and must not appear on the command line. The refreshed snapshot is committed like any design-export refresh.
46
35
 
47
- Two destinations; **ask the user which** (and the target space/parent/formats) - they are not mutually
48
- exclusive:
36
+ ### Guardrails
49
37
 
50
- ### A) Confluence - live & idempotent (`scripts/publish-confluence.py`)
38
+ - The skill only adds: its writes are limited to `localizations/` and `resources/`.
39
+ - Legacy source, `design-export/` and `specs/` are never modified.
40
+ - `resources/` is a generated copy. Refresh it with the scripts; do not edit it by hand.
41
+
42
+ ## Option A: Confluence (live page)
51
43
 
52
44
  ```bash
53
- python3 scripts/publish-confluence.py --xml "$OUT/$SLUG.confluence.xml" --screen "<name>" \
54
- --screenshot "$OUT/screenshot.png" --overlay "$OUT/$SLUG.overlay.png" \
55
- --keyshots "$OUT/$SLUG.keyshots.manifest.json" \
56
- --attach "$OUT/$SLUG.localization.xlsx"
57
- # defaults: --base $CONFLUENCE_BASE_URL --space <SPACE> --parent <PARENT-ID> --title "<prefix> - <name>"
45
+ python3 scripts/publish-confluence.py \
46
+ --xml <slug>.confluence.xml --screen "<Screen name>" \
47
+ --overlay <slug>.overlay.png --screenshot screenshot.png \
48
+ --keyshots <slug>.keyshots.manifest.json \
49
+ --attach <slug>.localization.xlsx
58
50
  ```
59
- - **Target configuration.** Nothing about your wiki is baked in: pass `--base-url` / `--space` / `--parent`,
60
- or set `CONFLUENCE_BASE_URL` / `CONFLUENCE_SPACE` / `CONFLUENCE_PARENT` once in the environment. Without a
61
- target the script refuses rather than guessing. `--title-prefix` (env `LOCALIZATION_PAGE_PREFIX`) sets the
62
- wording of the default page title.
63
- - **Server/DC Bearer auth** (NOT Atlassian Cloud `user:token`): the PAT is read from the macOS keychain item
64
- **`CONFLUENCE_API_TOKEN`** and passed to `curl` via a `chmod 600` `-K` config (never on the argv). HTTP goes
65
- through `curl` so a corporate TLS proxy's trust store is honored (Python `urllib` fails cert verification
66
- there).
67
- - **Idempotent by title.** It looks the page up via the DB-backed `GET /rest/api/content?spaceKey=...&title=...`
68
- (NOT CQL `/content/search`, whose async index lags a fresh page and would create a duplicate). Found →
69
- **PUT `version+1`, updating the same page in place**; absent → **POST create** under `--parent`. Re-running
70
- the mapper on a screen refreshes its page; it never duplicates.
71
- - **Attachments.** `screenshot.png` + the overlay PNG are attached (create, or update-by-filename if present);
72
- the storage `<ac:image><ri:attachment ri:filename="...">` tags resolve to them. Filenames must match what
73
- `build-artifact.py` embedded (its basenames). **`--keyshots <manifest>`** attaches every per-key screenpiece
74
- PNG listed in `$SLUG.keyshots.manifest.json` (render-key-shots.py) - without it the Summary table's
75
- "Screenshot" cells reference attachments that don't exist and render as broken images.
76
- **`--attach <file>` (repeatable)** attaches any extra document
77
- - pass `$SLUG.localization.xlsx` (the CMS import sheet, build-spreadsheet.py); office/spreadsheet files also
78
- get a `view-file` macro appended to the body so they **render inline** under a "CMS transfer file" heading.
79
- - The skill asks the user for the space / parent / title when the environment doesn't already pin them.
80
- - `build-artifact.py ... --print-upload` prints the exact command (plus the raw Bearer `curl`) for copy-paste.
81
-
82
- ### B) <specs-repo> PR (in-repo record - optional)
83
-
84
- Place the canonical set under `localizations/<slug>/` and open an additive PR:
51
+
52
+ - **Target**: `--base` / `--base-url`, `--space` and `--parent`, or the variables `CONFLUENCE_BASE_URL`, `CONFLUENCE_SPACE` and `CONFLUENCE_PARENT`.
53
+ - **Title**: `--title` when given; otherwise `<prefix> - <screen>`, with the prefix taken from `--title-prefix`, then `LOCALIZATION_PAGE_PREFIX`, then the default `Localization`.
54
+ - **Token**: a Server / Data Center personal access token, sent as a Bearer header. The script tries `--token`, then `CONFLUENCE_API_TOKEN`, then the macOS keychain service given by `--keychain-name` (default `CONFLUENCE_API_TOKEN`). Use the keychain where possible, because other processes can read argv.
55
+ - **Transport**: `curl` carries every request. The Authorization header is written to a temporary config file with mode 600, handed over with `-K` and removed at the end; JSON bodies also travel through a temporary file.
56
+ - **Idempotent**: the lookup is `GET /rest/api/content?spaceKey=&title=&type=page&expand=version&limit=5`, which reads the database rather than the CQL index. A hit is updated with `PUT` and the next version number, reported as `updated (vN->vN+1)`; a miss is created with `POST` below the parent and reported as `created`. A second run lands on the same page id.
57
+ - **Attachments**: the screenshot first, then the overlay, then each `.png` listed in the keyshots manifest, then every `--attach` file. When a file of that name is already attached, a new version is uploaded to `.../child/attachment/<id>/data` instead of adding a second copy.
58
+ - **Inline spreadsheet**: an `--attach` file with the extension `.xlsx`, `.xls`, `.csv`, `.docx` or `.pdf` is additionally shown on the page: a heading "CMS Aktarım Dosyası" is appended, then one `view-file` macro per such file.
59
+ - **Dry run**: with `--dry-run` nothing is sent; the script prints base, space, parent, title, body size, the attachment list and the action it would take.
60
+ - **Output**: a JSON object with `action`, `id`, `title`, `attachments` and `url`.
61
+
62
+ Running `build-artifact.py --print-upload` shows the publish command that fits the mapping, plus a hand-written Bearer `curl` version of it that pulls the token out of the keychain. When neither route works, paste the storage XML into the Confluence editor and attach the images under their file names; the XML already points at them by name.
63
+
64
+ ## Option B: specs-repo pull request
65
+
66
+ Place the canonical files in `<specs-repo>/localizations/<slug>/`: the Markdown `<slug>.md`, the storage body `<slug>.confluence.xml`, the overlay `<slug>.overlay.png`, `screenshot.png`, the manifest `<slug>.keyshots.manifest.json` with its `keyshots/` folder, and the sheet `<slug>.localization.xlsx`. Then:
67
+
85
68
  ```bash
86
- SLUG=sign-up-account-details
87
- DST="$SPECS/localizations/$SLUG"; mkdir -p "$DST"
88
- cp "$OUT/$SLUG".{md,confluence.xml} "$OUT/$SLUG.overlay.png" "$OUT/screenshot.png" \
89
- "$OUT/$SLUG.keyshots.manifest.json" "$DST"/ 2>/dev/null
90
- [ -d "$OUT/keyshots" ] && cp -R "$OUT/keyshots" "$DST"/ # per-key screenpieces (md embeds them relatively)
91
- cd "$SPECS" && git switch -c "loc-map/$SLUG" \
92
- && git add "localizations/$SLUG" \
93
- && git commit -m "localizations: $SLUG reuse map" \
94
- && git push -u origin "loc-map/$SLUG" \
95
- && gh pr create --title "Localization reuse map - $SLUG" \
96
- --body "Generated by the localization-reuse-map skill; screenshot + overlay included." --fill
69
+ git checkout -b loc-map/<slug>
70
+ git add localizations/<slug>
71
+ git commit --message "localizations: <slug> reuse map"
72
+ git push --set-upstream origin loc-map/<slug>
73
+ gh pr create --body "<summary, counts, gate result>" --title "Localization reuse map - <slug>"
97
74
  ```
98
- - The `.md` is the human-readable map (images embedded by filename - keep the PNGs beside it so they render
99
- on GitHub). The `.confluence.xml` is the storage-format payload Confluence consumes.
100
- - **Merge policy:** additive (new files under `localizations/`) → auto-/admin-merge cleanly.
101
- - Need a file for the content team? Re-run `build-artifact.py` with `--docx` / `--pdf` / `--all` (CMS columns
102
- + overlay embedded). Keep these heavy binaries **out** of the PR - share them directly.
103
-
104
- ## Guardrails
105
- - **Additive only.** This flow creates files under `localizations/` and `resources/`; it never edits
106
- legacy source, `design-export/`, or `specs/`.
107
- - The `resources/` snapshot is **generated, not authored** - never hand-edit it; refresh it from
108
- `<resources-repo>` (its source of truth), same as `design-export/` is refreshed from Figma.
75
+
76
+ Because the PR adds files and touches nothing else, it merges without conflicts. Keep the large `.docx` and `.pdf` files out of it and send them to people directly.
77
+
78
+ A screen may go through both options. Before publishing, ask which option or options to use, the Confluence space, and which formats are wanted.