@mmerterden/multi-agent-pipeline 20.5.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 (81) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/LICENSE +10 -0
  3. package/docs/facts.json +5 -4
  4. package/install/_dev-only-files.mjs +1 -0
  5. package/manifest.json +84 -82
  6. package/package.json +4 -3
  7. package/pipeline/lib/confusables.json +100 -0
  8. package/pipeline/lib/normalize-text.mjs +48 -0
  9. package/pipeline/lib/outbound-gate.mjs +10 -1
  10. package/pipeline/lib/redact.mjs +4 -1
  11. package/pipeline/lib/vercel-deploy.sh +5 -7
  12. package/pipeline/multi-agent-refs/features/design-conformance.md +62 -64
  13. package/pipeline/scripts/_notices.mjs +11 -0
  14. package/pipeline/scripts/agent-guard.py +30 -1
  15. package/pipeline/scripts/gen-skills-index.mjs +1 -1
  16. package/pipeline/scripts/pre-commit-check.sh +59 -16
  17. package/pipeline/scripts/pre-push-check.sh +3 -3
  18. package/pipeline/scripts/run-ui-tests.sh +5 -5
  19. package/pipeline/scripts/website-deploy-commit.sh +3 -2
  20. package/pipeline/skills/.skill-manifest.json +39 -39
  21. package/pipeline/skills/shared/README.md +1 -1
  22. package/pipeline/skills/shared/external/agent-introspection-debugging/SKILL.md +1 -0
  23. package/pipeline/skills/shared/external/android-architecture/SKILL.md +2 -0
  24. package/pipeline/skills/shared/external/android-performance/SKILL.md +2 -0
  25. package/pipeline/skills/shared/external/android-security/SKILL.md +2 -0
  26. package/pipeline/skills/shared/external/backlog/BACKLOG.md +1 -1
  27. package/pipeline/skills/shared/external/backlog/SKILL.md +56 -33
  28. package/pipeline/skills/shared/external/ci-cd-pipelines/SKILL.md +1 -0
  29. package/pipeline/skills/shared/external/compose-components/SKILL.md +2 -0
  30. package/pipeline/skills/shared/external/compose-navigation/SKILL.md +3 -2
  31. package/pipeline/skills/shared/external/compose-testing/SKILL.md +2 -0
  32. package/pipeline/skills/shared/external/council/SKILL.md +1 -0
  33. package/pipeline/skills/shared/external/css-modern/SKILL.md +1 -0
  34. package/pipeline/skills/shared/external/database-patterns/SKILL.md +1 -0
  35. package/pipeline/skills/shared/external/evidence-github/SKILL.md +2 -0
  36. package/pipeline/skills/shared/external/evidence-registry/SKILL.md +2 -0
  37. package/pipeline/skills/shared/external/gradle-kotlin-dsl/SKILL.md +2 -0
  38. package/pipeline/skills/shared/external/html-semantic/SKILL.md +1 -0
  39. package/pipeline/skills/shared/external/humanizer/SKILL.md +1 -0
  40. package/pipeline/skills/shared/external/ios-coding-standard/SKILL.md +1 -0
  41. package/pipeline/skills/shared/external/ios-module-structure/SKILL.md +1 -0
  42. package/pipeline/skills/shared/external/ios-security/SKILL.md +2 -0
  43. package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +91 -283
  44. package/pipeline/skills/shared/external/localization-reuse-map/reference/format-and-output.md +119 -151
  45. package/pipeline/skills/shared/external/localization-reuse-map/reference/publish-and-snapshot.md +60 -90
  46. package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +119 -156
  47. package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-artifact.py +726 -787
  48. package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-spreadsheet.py +253 -288
  49. package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-annotations.py +243 -304
  50. package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-legacy-labels.py +88 -104
  51. package/pipeline/skills/shared/external/localization-reuse-map/scripts/publish-confluence.py +181 -235
  52. package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-key-shots.py +198 -263
  53. package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-overlay.py +461 -466
  54. package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-legacy-values.py +145 -151
  55. package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-new-values.py +123 -141
  56. package/pipeline/skills/shared/external/localization-reuse-map/scripts/scan-screen-keys.py +146 -157
  57. package/pipeline/skills/shared/external/localization-reuse-map/scripts/snapshot-resources.sh +22 -19
  58. package/pipeline/skills/shared/external/localization-reuse-map/scripts/verify-map.py +156 -140
  59. package/pipeline/skills/shared/external/nextjs-app-router/SKILL.md +1 -0
  60. package/pipeline/skills/shared/external/play-store-review/SKILL.md +2 -0
  61. package/pipeline/skills/shared/external/python-patterns/SKILL.md +1 -0
  62. package/pipeline/skills/shared/external/react-best-practices/SKILL.md +1 -0
  63. package/pipeline/skills/shared/external/rest-api-design/SKILL.md +1 -0
  64. package/pipeline/skills/shared/external/retrofit-networking/SKILL.md +2 -0
  65. package/pipeline/skills/shared/external/room-database/SKILL.md +2 -0
  66. package/pipeline/skills/shared/external/search-first/SKILL.md +1 -0
  67. package/pipeline/skills/shared/external/signal-community/SKILL.md +2 -0
  68. package/pipeline/skills/shared/external/skill-creator/SKILL.md +80 -41
  69. package/pipeline/skills/shared/external/skill-creator/audit.md +63 -59
  70. package/pipeline/skills/shared/external/skill-creator/checklist.md +28 -20
  71. package/pipeline/skills/shared/external/skill-creator/examples.md +40 -40
  72. package/pipeline/skills/shared/external/skill-creator/label-check.md +48 -36
  73. package/pipeline/skills/shared/external/skill-creator/scripts/audit-panel.js +91 -100
  74. package/pipeline/skills/shared/external/skill-creator/template.md +51 -39
  75. package/pipeline/skills/shared/external/tailwind-css/SKILL.md +1 -0
  76. package/pipeline/skills/shared/external/testing-backend/SKILL.md +1 -0
  77. package/pipeline/skills/shared/external/typescript-patterns/SKILL.md +1 -0
  78. package/pipeline/skills/shared/external/vue-composition/SKILL.md +1 -0
  79. package/pipeline/skills/shared/external/web-accessibility/SKILL.md +1 -0
  80. package/pipeline/skills/shared/external/web-performance/SKILL.md +1 -0
  81. package/pipeline/skills/shared/external/web-testing/SKILL.md +1 -0
@@ -1,6 +1,8 @@
1
1
  ---
2
2
  name: compose-navigation
3
3
  description: "Implement type-safe navigation in Jetpack Compose using Navigation 2.8+ with @Serializable routes, NavHost, typed composable routes, nested navigation graphs, deep links, BackHandler, type-safe argument passing, and bottom navigation with NavigationBar integration. Use when building Compose navigation, adding new screens, handling deep links, or implementing bottom nav patterns."
4
+ metadata:
5
+ source: multi-agent-pipeline
4
6
  ---
5
7
 
6
8
  # Compose Navigation
@@ -487,7 +489,7 @@ fun EditScreen(
487
489
 
488
490
  ## Review Checklist
489
491
 
490
- - [ ] All routes are `@Serializable` data classes or objects
492
+ - [ ] All routes are `@Serializable` data classes or objects, and the kotlinx-serialization plugin is applied in build.gradle.kts
491
493
  - [ ] `NavController` stays at NavHost level; screens receive navigation lambdas
492
494
  - [ ] Route arguments are primitives or enums only
493
495
  - [ ] Bottom navigation uses `saveState`/`restoreState`/`launchSingleTop`
@@ -496,4 +498,3 @@ fun EditScreen(
496
498
  - [ ] Predictive back is enabled in manifest for Android 14+
497
499
  - [ ] Nested graphs use `navigation<>()` with explicit start destination
498
500
  - [ ] No navigation calls inside composable body without guard
499
- - [ ] kotlinx-serialization plugin is applied in build.gradle.kts
@@ -1,6 +1,8 @@
1
1
  ---
2
2
  name: compose-testing
3
3
  description: "Test Jetpack Compose UI with createComposeRule, semantic matchers (onNodeWithText, onNodeWithTag, onNodeWithContentDescription), actions (performClick, performTextInput, performScrollTo), assertions (assertIsDisplayed, assertExists, assertTextEquals), screenshot testing with Roborazzi or Paparazzi, ViewModel testing with Turbine, TestDispatcher, and runTest patterns. Use when writing Compose UI tests, ViewModel tests, or screenshot tests."
4
+ metadata:
5
+ source: multi-agent-pipeline
4
6
  ---
5
7
 
6
8
  # Compose Testing
@@ -7,6 +7,7 @@ description: |
7
7
  Triggers on: "should we X or Y", "second opinion", "is this worth it",
8
8
  "ship now or wait", requests for dissent or multiple perspectives.
9
9
  metadata:
10
+ source: multi-agent-pipeline
10
11
  version: 1.0.0
11
12
  ---
12
13
 
@@ -2,6 +2,7 @@
2
2
  name: css-modern
3
3
  description: "Modern CSS: container queries, nesting, @layer, Grid/Flexbox patterns, custom properties, animations, and transitions. Use when writing or reviewing modern CSS: layout, container queries, custom properties, animation."
4
4
  metadata:
5
+ source: multi-agent-pipeline
5
6
  tags: [css, grid, flexbox, container-queries, nesting, animations, frontend]
6
7
  version: "2025.1"
7
8
  ---
@@ -2,6 +2,7 @@
2
2
  name: database-patterns
3
3
  description: "Database patterns: SQL best practices (PostgreSQL), indexing, query optimization, migrations, schema design, ORMs (SQLAlchemy, Prisma, Drizzle). Use when designing a schema, writing migrations, or a query needs optimising."
4
4
  metadata:
5
+ source: multi-agent-pipeline
5
6
  tags: [database, sql, postgresql, prisma, drizzle, sqlalchemy, backend]
6
7
  version: "2025.1"
7
8
  ---
@@ -1,6 +1,8 @@
1
1
  ---
2
2
  name: evidence-github
3
3
  description: Search GitHub for facts an analysis can cite - a known issue in a dependency, the PR that fixed it, what a release actually changed, prior art for a pattern. Use when a spec, review or triage needs to know whether a problem is already known upstream.
4
+ metadata:
5
+ source: multi-agent-pipeline
4
6
  ---
5
7
 
6
8
  # GitHub as evidence
@@ -1,6 +1,8 @@
1
1
  ---
2
2
  name: evidence-registry
3
3
  description: Check a package's real version, deprecation status and release notes on npm, PyPI, Maven Central or Swift Package Index. Use when a spec or review depends on what a dependency currently is rather than what a lockfile said months ago. Keyless HTTP, works on every platform.
4
+ metadata:
5
+ source: multi-agent-pipeline
4
6
  ---
5
7
 
6
8
  # Package registries as evidence
@@ -1,6 +1,8 @@
1
1
  ---
2
2
  name: gradle-kotlin-dsl
3
3
  description: "Configure Android builds with Gradle Kotlin DSL, version catalogs (libs.versions.toml), convention plugins (build-logic module), common configurations for android library/compose/testing, build variants, flavors, signing configs, R8/ProGuard rules, and build performance optimization including configuration cache and parallel execution. Use when setting up Gradle builds, adding dependencies, creating convention plugins, or optimizing build times."
4
+ metadata:
5
+ source: multi-agent-pipeline
4
6
  ---
5
7
 
6
8
  # Gradle Kotlin DSL
@@ -2,6 +2,7 @@
2
2
  name: html-semantic
3
3
  description: "Semantic HTML5: elements, forms with validation and accessibility, SEO best practices, structured data, Open Graph, Twitter Cards. Use when writing or reviewing markup, forms or document structure for semantics, accessibility and SEO."
4
4
  metadata:
5
+ source: multi-agent-pipeline
5
6
  tags: [html, semantic, seo, forms, open-graph, structured-data, frontend]
6
7
  version: "2025.1"
7
8
  ---
@@ -6,6 +6,7 @@ description: |
6
6
  mechanical structure, and sycophantic tone. Use when editing text, writing
7
7
  documentation, commit messages, PR descriptions, or any user-facing content.
8
8
  metadata:
9
+ source: multi-agent-pipeline
9
10
  version: 1.2.0
10
11
  ---
11
12
 
@@ -3,6 +3,7 @@ name: ios-coding-standard
3
3
  description: "The iOS coding-standard rule registry: 99 stable-ID rules across readability, security, service layer, business rules, concurrency, testing, module boundaries, naming and visibility, each with a severity and enforcement kind. Use when writing or reviewing Swift and you need the project rule rather than an opinion, or on a persistence, logging or business-rule-placement question."
4
4
  user-invocable: true
5
5
  metadata:
6
+ source: multi-agent-pipeline
6
7
  standards-registry: references/rules.yml
7
8
  ---
8
9
 
@@ -3,6 +3,7 @@ name: ios-module-structure
3
3
  description: "The ios-module-structure rule registry: stable-ID rules over where a declaration lives, what its file is called and what its folder must contain beside it, plus a checker that runs them against one module. Use when auditing or refactoring a module's tree, or when a review wants a rule ID rather than a preference about layout."
4
4
  user-invocable: true
5
5
  metadata:
6
+ source: multi-agent-pipeline
6
7
  standards-registry: references/rules.yml
7
8
  ---
8
9
 
@@ -1,6 +1,8 @@
1
1
  ---
2
2
  name: ios-security
3
3
  description: "Secure iOS apps with Keychain Services, CryptoKit encryption, biometric authentication (Face ID, Touch ID), Secure Enclave key storage, LAContext, App Transport Security (ATS), certificate pinning, data protection classes, and secure coding patterns. Use when implementing app security features, auditing privacy manifests, configuring App Transport Security, securing keychain access, adding biometric authentication, or encrypting sensitive data with CryptoKit."
4
+ metadata:
5
+ source: multi-agent-pipeline
4
6
  ---
5
7
 
6
8
  # iOS Security
@@ -1,294 +1,102 @@
1
1
  ---
2
2
  name: localization-reuse-map
3
- description: >-
4
- Builds a per-screen localization-reuse map for one redesigned screen (iOS, Android, web): pairs each
5
- UI element's new key and values with the legacy key per platform and the live CMS copy from Figma Dev
6
- Mode annotations, recommends reuse, review or new for the content team to confirm, renders a key-to-UI
7
- overlay, publishes a Confluence page, and flags error and dynamic keys the registry misses. Use when
8
- mapping one redesigned screen's strings to legacy ones. Triggers - localization reuse map, map old
9
- keys to new keys, reuse legacy translations, CMS copy from Figma annotations, localization excel, CMS
10
- import sheet, /localization-reuse-map.
3
+ description: "Localization reuse map for one redesigned screen on iOS, Android and web: new keys set against each platform's legacy key, legacy values and the Figma-annotation CMS copy, with a reuse / review / new verdict, a key overlay, per-key shots, a CMS import sheet and a Confluence page. Use it to map old keys onto new ones, reuse legacy translations, pull CMS copy out of Figma annotations, or build a localization excel or CMS import sheet; also answers to /localization-reuse-map. Not for several screens at once, and not for minting or defining new keys (the resource-authoring flow)."
4
+ metadata:
5
+ version: 1.0.0
6
+ source: multi-agent-pipeline
11
7
  ---
12
8
 
13
- # Localization Reuse Map - old↔new localization map for one screen
9
+ # localization-reuse-map
14
10
 
15
- Produces, for **one redesigned screen**, the table content management needs: every UI element with its
16
- **new** key + translations, its **legacy** key + translations per platform, and the content team's **actual
17
- CMS copy** (Figma annotations), plus a **recommended** reuse verdict, a **visual key↔UI overlay**, and a
18
- **per-key screenpiece** (a red-box crop showing where that key lives on screen) embedded in each table row -
19
- published **live** as a Confluence page. The skill **recommends; the content team decides**.
11
+ One run covers one redesigned screen on every platform where it exists. The output is a table with a row per UI element that answers a single question for the content team: can this string reuse what the legacy app already ships, does it need a human look, or is it genuinely new?
20
12
 
21
- **Detail:** [reference/sources-and-recipes.md](reference/sources-and-recipes.md) (data model + exact
22
- per-source retrieval recipes, incl. CMS annotations + the implemented-screen scan) ·
23
- [reference/format-and-output.md](reference/format-and-output.md) (verdict taxonomy + mapping JSON schema +
24
- CMS columns + Confluence output) · [reference/publish-and-snapshot.md](reference/publish-and-snapshot.md)
25
- (publish live to Confluence + the `localizations/` PR + snapshot the resources in-repo).
26
- **Input shape:** [example-mapping.json](example-mapping.json).
13
+ Scope rules that hold for every run:
27
14
 
28
- ## Project configuration - resolve these first
29
- Nothing about any one company is baked in. Establish these once per project (ask the user, or read them from
30
- the project's own docs) and reuse them for every screen:
15
+ - **One screen per run.** Rolling several screens into one map is out of scope.
16
+ - **Recommend, never decide.** The verdict column only suggests. The decision belongs to the content team, so no verdict is ever presented as settled.
17
+ - **Never invent a value.** A backend serves the legacy translations; either fetch them, or keep the Old column empty and add a note. CMS copy comes from the Figma annotation or stays blank. A blank cell is better than a guess.
18
+ - **CMS copy is additive.** The annotation holds the copy the content team signed off. It sits in its own columns and never overwrites `new`. When it differs from `new`, the difference is shown with a warning mark, not hidden.
19
+ - **Figma live first.** CMS annotations and overlay geometry are read from Figma, over REST or through Figma MCP. The design export kept in the repo may be out of date, so it is used only if neither live route works.
20
+ - **Read-only everywhere** except two writes: the Confluence page and, optionally, a pull request that adds `localizations/<slug>/` to the specs repo.
21
+ - **Components are mapped once.** When a reusable component owns a key, the row is flagged `(owned by <Component>)` instead of being mapped again on each screen where the component appears.
22
+ - **Chrome language.** Author-written `element` labels and `note` text follow `--ui-lang`; keys and translation values stay verbatim.
31
23
 
32
- | What | Used for | Example |
33
- |---|---|---|
34
- | Specs repo + design-export path | screen frames, `components-used.md`, `screenshot.png`, `tree.json` | `design-export/<project>/screens/<slug>/` |
35
- | Resources root | authored/suggested new values + the component registry | `resources/Localization/Suggested/<Key>.json` |
36
- | Legacy label endpoint + headers | refreshing the legacy value snapshot | `--endpoint` + `--headers-file` |
37
- | Legacy key prefix (if any) | display-only re-prefixing in the table | mapping `legacyKeyPrefix` |
38
- | Web i18n convention (legacy AND new, if either is a web app) | which call form + key-file shape the tracer/scanner look for | `t('key')` / `$t('key')` reading `locales/<lang>.json` |
39
- | Confluence base URL / space / parent | publishing | `CONFLUENCE_BASE_URL` / `_SPACE` / `_PARENT` |
40
- | CMS bucket taxonomy | the spreadsheet's Property Group / Module | `--taxonomy cms-taxonomy.json` |
41
- | Document language | template chrome + author-written fields | `--ui-lang en` (default) / `tr` |
42
-
43
- Secrets stay in a keychain item or a `--headers-file`, never in the mapping, the docs, or argv.
44
-
45
- ## Scope
46
- One screen per run, every platform the screen exists on; publishes the map **live to Confluence** (idempotent
47
- update-in-place) and optionally to the specs repo's `localizations/` as a PR - the user is asked which target
48
- + space/formats. Out: cross-screen rollups and minting/defining new keys (that is the resource-authoring flow,
49
- not this skill).
50
-
51
- ## Invariants - read before running
52
- - **One screen per run.** The unit is a screen; keys owned by a *reusable component* are flagged, not
53
- re-mapped - they are mapped once where that component is mapped.
54
- - **The skill recommends; the content team decides** the reuse call. Never present a verdict as final.
55
- - **Never invent a legacy value.** Legacy translations are backend-served - fetch them or leave the Old
56
- column blank with a note. Blank beats a guess. (See sources-and-recipes "Legacy translation values".)
57
- - **Never invent a CMS value.** The CMS column is the content team's *actual* copy - fetch it from the Figma
58
- annotation or leave it blank. The annotation is authoritative as their final copy; it is shown
59
- **additively** (it never overwrites the `new` value), and a CMS≠new difference is surfaced (⚠), not hidden.
60
- - **Figma is live-first.** The CMS annotations + overlay are fetched from the Figma **REST API** (or MCP),
61
- falling back to the in-repo design export only when neither is reachable - because Figma is edited
62
- continuously and the snapshot can be stale.
63
- - **Read-only across every source.** This skill produces an artifact; it edits no repo and defines no key.
64
- (The only writes are the Confluence page it publishes and the optional `localizations/` PR.)
65
- - **Author the document in the content team's language.** The author-supplied `element` labels and `note`
66
- annotations must match the chrome language (`--ui-lang`) - they are the only free text in the doc, so a
67
- mixed-language run reads as half-translated. Only keys and translation values stay verbatim.
68
-
69
- ## Sources (self-contained - no other skill required)
70
- Retrieval is a **cross-reference, not a skim** - that distinction is the whole skill. Full recipes:
71
- [reference/sources-and-recipes.md](reference/sources-and-recipes.md).
72
- - **New keys** - cross-reference three: the design export's `screens/<frame>/components-used.md` (which
73
- components, **all** state frames) **×** each component's registry `<node>.json → localizationKeys` (keys it
74
- renders internally, often absent from screen code) **×** the screen's code (what it wires). Union; tag
75
- screen-specific / component-owned / shared.
76
- - **New values** - the authored per-key source (`Suggested/<Key>.json`, 8 langs):
77
- `scripts/resolve-new-values.py --langs all --catalog <shipped catalog>`. **Always pass
78
- `--catalog`.** The Suggested tree is a generated snapshot and goes stale the moment a key is
79
- added upstream, so a missing file means "not in this snapshot", never "this key is
80
- unauthored". With the catalog the script resolves the value anyway and reports that the
81
- snapshot needs `snapshot-resources.sh`; without it every stale key reads as needing authoring.
82
- - **CMS values (content team's actual copy)** - Figma Dev Mode annotations, fetched **live**:
83
- `scripts/fetch-annotations.py --mapping <map>.json` (REST primary → `--from-mcp` → `--local` fallback).
84
- Map each annotation to its row by `nodeId` → fill `cms:{tr,en}`. Blank when not yet annotated.
85
- - **Error / dynamic keys the cross-reference misses** - scan the *implemented* screen:
86
- `scripts/scan-screen-keys.py --screen-path <dir>` → error/validation/alert + dynamic/`String(format:)`/
87
- variable-`.localized` keys (categorized). Fold the `error`/`dynamic` hits into the mapping.
88
- - **Legacy keys** - **trace the screen**, don't skim: iOS VC→VM→**cell presentation models**
89
- (`"Key".localized`); Android layout `@string/Key`; web component→ `t('key')`/`$t('key')` call site,
90
- key resolved against the legacy app's `locales/<lang>.json` (or equivalent i18n catalog). The screen's
91
- spec names the legacy entry class/component.
92
- - **Legacy values** - the in-repo label snapshot (offline, no network per run):
93
- `scripts/resolve-legacy-values.py --snapshot-root <resources>/Localization/Legacy --keys "..." --langs all`.
94
- Refresh the snapshot (the only network step) with `scripts/fetch-legacy-labels.py --endpoint ...
95
- --headers-file ...`. `--live` to verify; `--plist-root` last-resort.
96
- - **A legacy key prefix** the backend stores but the app strips is kept out of the mapping and re-added for
97
- display via the mapping's `legacyKeyPrefix` (`Continue` → displayed `Mobile-Continue`).
98
- - **Screenshot** - the design export's `screens/<frame>/screenshot.png`.
99
- - **Overlay (key↔UI image)** - `scripts/render-overlay.py --mapping <map>.json` (REST primary → `--spec`
100
- MCP). Embedded in the page as a "key map" section; needs headless Chrome (falls back to `.html`).
101
- - **Per-key screenpieces (keyshots)** - `scripts/render-key-shots.py --mapping <map>.json` (same REST /
102
- `--spec` modes and the same row↔node matcher as the overlay): one red-box crop per matched row →
103
- `keyshots/keyshot__<NN>__<key>.png` + `<slug>.keyshots.manifest.json`; the table's "Screenshot"
104
- cells embed them. Unmatched rows render " - ", never a guessed crop.
105
- - **Document language** - `--ui-lang en` (default) or `tr`.
106
-
107
- *Figma: for **structure** (components, nodes, screenshot) the in-repo design export is the source. For **CMS
108
- annotations + the overlay** the live **REST API** is primary (token in keychain `FIGMA_ACCESS_TOKEN`) with
109
- **MCP** (Dev Mode) as the no-rate-limit alternative; the export is the offline fallback.*
110
-
111
- ## Decision rules
112
- - Verdict per row: **reuse** (same function + same/equivalent value, or a clean shared key on both legacy
113
- platforms) · **review** (same function but value / key / cross-platform drift) · **new** (no legacy
114
- counterpart). Full table in format-and-output.md.
115
- - Key owned by a reusable component, not the screen → flag it (`(owned by ...)`), don't double-map.
116
- - Legacy keys differ across the platforms present (iOS/Android/web) → keep every column that has one,
117
- default to **review** (drift to resolve).
118
- - A `tr` value equal to its `en` value → note it (untranslated) regardless of verdict.
119
- - **CMS value present and ≠ the `new` value** → the renderer marks it ⚠; call it out in the note (the content
120
- team's final copy diverged from the mock). CMS is additive context for the verdict, not a verdict input.
121
- - **Dynamic key** that can't be statically resolved → map it with `verdict: review` + a "dynamic - enumerate
122
- at runtime" note; never drop it.
123
-
124
- ## Procedure
24
+ It is not a key-minting tool: new keys and their values belong to the resource-authoring flow.
125
25
 
126
- *Ask the user up front for what's missing* (interactive): the implemented **screen path**, the **Figma
127
- file key + node-ids** (or confirm the design-export slug), and at publish time the **Confluence target**
128
- (space / parent / formats). Don't block on what you can derive; ask only for gaps.
26
+ ## Project settings
129
27
 
130
- 1. **Enumerate new keys + elements (cross-reference).** From the design export get the screen's components
131
- (all state frames) + screenshot; read each component's registry `localizationKeys`; read the screen's
132
- code for wired keys. Union → one element per key, tagged screen/component/shared.
133
- 2. **Scan the implemented screen for the keys the cross-reference misses** (script):
134
- `python3 scripts/scan-screen-keys.py --screen-path <dir>` → fold the **error** + **dynamic** hits into the
135
- element list (the static union is blind to them); diff `static` hits vs the registry union.
136
- 3. **Resolve new values** (script): `python3 scripts/resolve-new-values.py --resources-root <resources>
137
- --keys "<all keys>" --langs all --catalog <shipped catalog>`. Only a key absent from BOTH the
138
- snapshot and the catalog is genuinely unauthored; anything the catalog resolved means the
139
- snapshot is stale (refresh it, do not open authoring requests).
140
- 4. **Fetch CMS copy from Figma annotations** (script): `python3 scripts/fetch-annotations.py --mapping
141
- <map>.json` (REST primary → `--from-mcp` → `--local`). Map each annotation to its row by `nodeId`
142
- (store `cmsNodeId`) → fill `cms:{tr,en}`. Leave blank where unannotated; flag TR-without-EN.
143
- 5. **Trace legacy keys, every platform** (sources-and-recipes recipes): the spec's legacy reference →
144
- VM → cell presentation models (iOS); layout `@string/` (Android). Don't stop at the VM.
145
- 6. **Resolve legacy values** (script): `python3 scripts/resolve-legacy-values.py --snapshot-root
146
- <resources>/Localization/Legacy --keys "<legacy keys>" --langs all` (offline snapshot; refresh it with
147
- `fetch-legacy-labels.py` when needed). Absent → leave blank + note.
148
- 7. **Assemble the mapping JSON** (schema in format-and-output.md) - one row per element: new/legacy keys +
149
- 8-lang `new`/`legacy` values + `cms`/`cmsNodeId` + a recommended `verdict` + `note`. Add `screenshot`,
150
- `figmaFileKey`, and `legacyKeyPrefix` when the backend uses one.
151
- 8. **Render the key↔UI overlay** (script): `python3 scripts/render-overlay.py --mapping <map>.json --out
152
- <dir>` → `<slug>.overlay.png`; set the mapping's top-level `overlay` to that filename. **The overlay does
153
- NOT need annotations** - with zero CMS annotations it still maps every keyed element to its spot on the
154
- screen (gray "awaiting copy" cards). **Card numbers = table row numbers:** each card (and its dot) is
155
- numbered by the matched row's position in `mapping.rows`, i.e. the same number `build-artifact` prints in
156
- the table - so a mark on the screen and its table row always agree (they are NOT renumbered by layout
157
- order). Pick the geometry source in this order:
158
- - **REST** (`--mapping` with `figmaFileKey` + the real **frame node**): full per-label geometry. Get the
159
- frame's true node-id from the design export screen's `tree.json` **root `nodeId`** (the folder-name
160
- suffix and any id in the old map are often stale/wrong - verify the node's text is actually this
161
- screen's). A frame may include background content (the screen shown as a sheet over another) - the
162
- non-matching nodes are dropped automatically.
163
- - **`--spec` (screenshot-anchored, no token / when REST geometry is noisy or unavailable)** - the reliable
164
- fallback the agent drives: read each visible element's box **off the clean screenshot**, set that row's
165
- `cmsNodeId` to a synthetic id (`n1`, `n2`, ...), and emit a spec page `{fileKey, pages:[{nodeId, image:
166
- "<screenshot dataURL>", frame:{w,h}, nodes:[{id:"n1", characters:"<on-screen text>", x,y,w,h, side}]}]}`.
167
- `render-overlay` anchors each card by `cmsNodeId` → exact key, positions from the real render.
168
- - **`side: "left" | "right"` per node (default left)** - put a card on the side of the frame its target sits
169
- on, so the connector stays short and doesn't cross the whole screen. Essential for **two-column layouts**:
170
- flag the right-column entries `"side":"right"` and they render as a right-hand card column with mirrored
171
- connectors; the left column stays left. Cards on each side are numbered and stacked independently.
172
- - The export's `tree.json` alone is **component-instance level** (no per-label geometry) - use it for the
173
- frame node-id, not for label boxes. No Chrome → `.html` fallback.
28
+ Nothing company-specific is built in. Establish these once per project (read them from the project's docs or ask), then reuse them for every screen:
174
29
 
175
- **Mark component-owned keys on the overlay too.** A screen is mostly reusable-component instances
176
- (list rows, promotion grids, summary cards) whose keys the screen code never wires - but the content team
177
- still sees that copy on this screen. Map them here (this is where the component is used), each flagged
178
- `(owned by <Component>)` in its `note`, and anchor them on the screenshot. Pull the component's keys from
179
- its registry `<node>.json → localizationKeys`. These are the keys a plain screen-code scan is blind to.
180
-
181
- **One screen, several states → several screenshots, one composite overlay.** A key like an "Added" badge or
182
- a "Remove" link only appears in one state, not the default mock. Render one overlay per state (each
183
- `--spec` a different screenshot with that state's `cmsNodeId`s - `render-overlay` cards only the rows whose
184
- node matches that spec, so reusing a row's `cmsNodeId` across states is fine), then **vertically
185
- composite** the per-state overlays into one PNG (pad to max width, dark `#1e1e1e` gutter) and set that as
186
- the mapping's `overlay` - `build-artifact` embeds a single overlay, so the composite is how all states
187
- reach the doc. Keep the standalone per-state PNGs beside it.
188
-
189
- **Isolate one component variant when you want a clean render of it:** exported **library** node-ids go
190
- **stale** once the Figma file re-versions (`/components` may return 0, `/images` returns `null`). Don't
191
- chase them - the current screen **frame** node still renders, so walk its live node tree
192
- (`GET /files/<key>/nodes?ids=<frame>&depth=6`) for the `INSTANCE` whose `name` is the component, render
193
- **that** instance id, and take per-label boxes from its own text nodes' `absoluteBoundingBox` (× the render
194
- `scale`). No live token / render fails → just **crop the variant out of a state screenshot**; a crop of the
195
- real card is an equally valid variant image.
196
-
197
- **Interactive - guide the developer here:** if no annotations were found, say so and tell them you'll map
198
- keys to UI by reading the screen; confirm the **screen state** (which mock/screenshot) and that the
199
- anchored element list looks right before rendering.
200
- 9. **Render the per-key screenpieces (keyshots)** (script): `python3 scripts/render-key-shots.py --mapping
201
- <map>.json --out <dir> [--slug <slug>]` → `keyshots/keyshot__<NN>__<key>.png` (one red-box crop per
202
- matched row; **NN = the table row number**, same numbering as the overlay cards) +
203
- `<slug>.keyshots.manifest.json`. Reuses the overlay's geometry inputs verbatim: REST (visible text nodes
204
- only - ancestor-visibility + opacity filtered) or the same `--spec` file(s) you built for step 8
205
- (repeatable - one per screen state; `image` may be a file path, e.g. the committed `screenshot.png`).
206
- Set the mapping's top-level `keyshots` to the manifest filename; rows the geometry can't match land in
207
- the manifest's `missing` and their table cells render " - ". Detail in
208
- [reference/format-and-output.md](reference/format-and-output.md).
209
- 10. **Build the artifacts** (script): `python3 scripts/build-artifact.py <mapping>.json --out <dir>` →
210
- `<slug>.md` + `<slug>.confluence.xml` + `<slug>.preview.html` (CMS columns + the overlay key-map section
211
- + the per-key "Screenshot" table cells when `keyshots` is set).
212
- Add `--docx` / `--pdf` (or `--all`) for the content team - `.docx` is pure-stdlib/offline; `.pdf` uses an
213
- installed renderer (LibreOffice → Chrome → wkhtmltopdf), skipped with a note if none is present.
214
- 11. **Build the CMS import spreadsheet** (script): `python3 scripts/build-spreadsheet.py <mapping>.json --out
215
- <dir>` → `<slug>.localization.xlsx` - a **real Excel file** (an OOXML zip written with the Python **stdlib
216
- only**, no pip / no network, the same trick `build-artifact.py` uses for `.docx`; add `--csv` for a CSV
217
- alongside, `--csv-only` when Excel isn't wanted). One row per key in the content team's CMS-import columns:
218
- **Channel** · **Property Group** / **Property Module** (deduced from the key namespace - bucket names come
219
- from `--taxonomy`; a row overrides with `propertyGroup` / `propertyModule`) · **Key** (our `newKey`,
220
- component tag stripped) · **EN / TR / AR Value** (our authored `new` values) · **Anotation EN / TR** (the
221
- content team's CMS Figma annotations - `cms.en` / `cms.tr`). This file is attached to the Confluence page
222
- at publish (step 13). Format detail in [reference/format-and-output.md](reference/format-and-output.md).
223
- 12. **Fidelity check - GATE before publish** (script): `python3 scripts/verify-map.py --mapping <map>.json
224
- --screen-path <dir> [--resources-root <res>]`. Reconciles the map against the **real screen code**:
225
- **UNVERIFIED** keys (a `newKey` not wired anywhere) → fix or drop; **wired-but-unmapped** keys (a
226
- localization-key reference in code with no row) → add rows, it's a coverage gap; **value gaps** /
227
- **untranslated** (tr==en) → resolve or note. Exit code 2 = hard issues - don't publish until clean (or a
228
- waiver is conscious). This is what catches a stale/typo'd key or a missed downstream state before it ships.
229
- 13. **Publish** ([reference/publish-and-snapshot.md](reference/publish-and-snapshot.md)) - ask the user which
230
- target(s): **(A) Confluence, live & idempotent** - `python3 scripts/publish-confluence.py --xml
231
- <slug>.confluence.xml --screen "<name>" --screenshot screenshot.png --overlay <slug>.overlay.png
232
- --keyshots <slug>.keyshots.manifest.json --attach <slug>.localization.xlsx` (Server/DC Bearer from the
233
- keychain; base URL / space / parent from flags or `CONFLUENCE_*` env; updates the screen's page **in
234
- place** on re-run). The `--attach` file rides along as a page attachment and, being a spreadsheet, also
235
- gets a `view-file` macro so it **renders inline**. **(B) specs-repo PR** - copy the canonical set (incl.
236
- `<slug>.localization.xlsx`, the manifest and `keyshots/`) into `localizations/<slug>/` and open an
237
- additive PR.
238
-
239
- *(Pipeline - runs **after** the screen spec: dev generates spec → generates this map → publishes. Resources
240
- are read from the in-repo snapshot when present; refresh it with `scripts/snapshot-resources.sh`.)*
241
-
242
- ## Verification
243
- - **Run `verify-map.py` (the fidelity gate)** - it reconciles the map against the real screen code and is
244
- the scripted form of "counts must reconcile": every `newKey` is wired (or component/dynamic-exempt), no
245
- localization-key reference in code is left unmapped, no fabricated/empty values. Fix what it flags before
246
- publishing. `scan-screen-keys.py` error/dynamic hits must be accounted for as rows.
247
- - Every row has a `verdict`; no Old **or CMS** value is fabricated (blank or sourced only).
248
- - `build-artifact.py` runs clean; the `.md` carries the header + legend + totals + the fixed column order
249
- including the CMS columns.
250
- - The overlay PNG was produced (or a `.html` fallback noted) and is referenced by the mapping's `overlay`.
251
- - **The keyshots were rendered** - `render-key-shots.py` wrote one PNG per matched row, the mapping's
252
- `keyshots` names the manifest, every non-" - " Screenshot cell resolves to a file that exists, and nothing in
253
- `keyshots/` is orphaned. Renaming a key or reordering rows renumbers `NN`: **re-render, never hand-rename.**
254
- - **The CMS spreadsheet was built** - `build-spreadsheet.py` wrote `<slug>.localization.xlsx` with one row per
255
- key (row count = `mapping.rows`), the fixed 9 columns, and it is `--attach`ed at publish. Values come from
256
- `new`, annotations from `cms` - never fabricated (blank where unsourced, same rule as the doc).
257
- - Publishing is **idempotent**: a second `publish-confluence.py` run on the same screen reports `updated`
258
- (version-bump) of the **same** page id - never a duplicate.
259
-
260
- ## Pitfalls
261
- - A large specs repo's recursive Git tree truncates (~43k paths) - use `gh search code`, not the tree API.
262
- - New key namespace ≠ legacy flat key (`SignUpAccountDetails.ContinueButton` vs `Continue`) - "reuse" means
263
- reuse the *value* (or alias the legacy key), not that the strings match.
264
- - Form-field labels often live in shared input-cell models, not the screen file - the Android layout is the
265
- reliable place to read them.
266
- - Legacy values are backend-served - blank Old columns are an expected state, not an error. **Likewise blank
267
- CMS columns** - not every node is annotated yet.
268
- - Error/validation/alert + dynamic keys are invisible to the static cross-reference - run
269
- `scan-screen-keys.py`, or they silently vanish from the map.
270
- - **A rendered frame PNG is not guaranteed to share the frame's `absoluteBoundingBox` origin.**
271
- Building a `--spec` from REST geometry by subtracting the frame's absolute x/y from each text
272
- node's absolute x/y drifted every box by tens of pixels against the image the `/images`
273
- endpoint returned - boxes landed a row below their target, which looks plausible enough to
274
- ship and is wrong. Verify one crop by eye before trusting a batch, and prefer boxes read off
275
- the actual screenshot (the `--spec` path the skill documents) over coordinates derived from
276
- two different sources.
277
- - **A key missing from the Suggested snapshot is not an unauthored key.** The snapshot is a
278
- generated mirror; upstream additions land in the shipped catalog first. Concluding "needs
279
- authoring" from the snapshot alone produced a batch of authoring requests for keys that were
280
- already live in all eight languages - the values were recoverable from the catalog the whole
281
- time. Pass `--catalog`, and when it answers, refresh the snapshot rather than filing anything.
282
- - **Never anchor a keyshot by value across a whole file.** A row whose label isn't drawn on
283
- its own frames is tempting to recover by searching every frame for that text - but short
284
- labels ("Türkiye", "Cancel", "Continue") recur on unrelated screens, and the crop then
285
- boxes the wrong element with full confidence. That is strictly worse than " - ", because the
286
- content team believes it. Recover only via the row's own `cmsNodeId` (the node the content
287
- team annotated), and when you hand nodes to `render-key-shots --spec`, **omit
288
- `characters`** - with text present the renderer falls back to value matching and an
289
- unrelated row can bind to your node. "Text exists somewhere in the file" is not evidence
290
- that it is the same element.
291
- - Confluence **Server/DC → Bearer PAT** (keychain `CONFLUENCE_API_TOKEN`) is not Cloud `user:token`; page
292
- lookup uses the DB-backed `content?title=` query, not CQL (CQL's index lags a fresh page → duplicate).
293
- - Never commit a label-service token, Confluence PAT or Figma token into a mapping, a taxonomy file or these
294
- docs - keychain items and `--headers-file` exist for that.
30
+ | Setting | What it is | Example |
31
+ |---|---|---|
32
+ | Specs repo + design export | Screen frames, `components-used.md`, `screenshot.png`, `tree.json` | `design-export/<project>/screens/<slug>/` |
33
+ | Resources root | Suggested new values and the component registry | `resources/Localization/Suggested/<Key>.json` |
34
+ | Legacy label endpoint | For refreshing the legacy snapshot | `--endpoint` + `--headers-file` |
35
+ | Legacy key prefix | Display-only prefix the backend stores | mapping `legacyKeyPrefix`, e.g. `Mobile-` |
36
+ | Web i18n convention | For legacy and/or new web apps | `t('key')` / `$t('key')`, keys in `locales/<lang>.json` |
37
+ | Confluence target | Base URL, space, parent page | `CONFLUENCE_BASE_URL` / `CONFLUENCE_SPACE` / `CONFLUENCE_PARENT` |
38
+ | CMS taxonomy | Property Group / Module buckets in the spreadsheet | `--taxonomy cms-taxonomy.json` |
39
+ | Document language | Headings, legend and labels | `--ui-lang en` (default) or `tr` |
40
+
41
+ Secrets live only in a keychain item or a `--headers-file`. They never go into the mapping, the taxonomy file, the docs or a command line.
42
+
43
+ At the start, ask the user only for what is still unknown: where the implemented screen lives, the Figma file key and node-ids (or a confirmed design-export slug), and, once it is time to publish, the Confluence target and the formats wanted.
44
+
45
+ ## Where this sits
46
+
47
+ After the screen spec exists: screen spec, then this map, then publish. If the repo holds a snapshot, the registry and the Suggested files are taken from it (see [publish-and-snapshot](reference/publish-and-snapshot.md)).
48
+
49
+ ## Steps
50
+
51
+ The helpers in `scripts/` use nothing beyond the Python 3 standard library, apart from one bash script. Commands and how each source is traced are covered in [sources-and-recipes](reference/sources-and-recipes.md).
52
+
53
+ 1. **Enumerate new keys.** Take the union of three sources: the design export's `components-used.md` across every state frame, each component's registry `localizationKeys`, and the keys the screen code wires. Tag each key screen-specific, component-owned or shared. Every key is confirmed by comparing sources, never lifted from one of them.
54
+ 2. **Catch what the cross-reference misses.** Run `scan-screen-keys.py --screen-path <dir>`. Each `error` and `dynamic` hit becomes a mapping row; compare the `static` hits with the union from step 1.
55
+ 3. **New values.** Run `resolve-new-values.py --keys "<all>" --resources-root <res> --langs all --catalog <shipped catalog>`; `--catalog` is never optional.
56
+ 4. **CMS copy.** Run `fetch-annotations.py --mapping <map>.json`, falling back from REST to `--from-mcp` and then `--local`. Fill `cms` and `cmsNodeId` on each row, and send any TR without EN back to the content team.
57
+ 5. **Trace legacy keys** per platform: spec, then view controller, view model and cell presentation models on iOS; layout `@string/` on Android; call sites on web.
58
+ 6. **Legacy values.** Run `resolve-legacy-values.py --keys "<legacy>" --snapshot-root <res>/Localization/Legacy --langs all`; if the snapshot is out of date, `fetch-legacy-labels.py` refreshes it. A value that cannot be found stays blank and gets a note.
59
+ 7. **Assemble the mapping JSON**: one row per element, plus `screenshot`, `figmaFileKey` and `legacyKeyPrefix` where they apply. Schema: [format-and-output](reference/format-and-output.md).
60
+ 8. **Overlay.** Run `render-overlay.py --out <dir> --mapping <map>.json`; it produces `<slug>.overlay.png`, which the mapping's `overlay` field then names.
61
+ 9. **Key screenshots.** Run `render-key-shots.py --out <dir> --mapping <map>.json [--slug]`; it fills `keyshots/` and writes the manifest, which the mapping's `keyshots` field then names.
62
+ 10. **Artifacts.** `build-artifact.py <map>.json --out <dir>` (add `--docx`, `--pdf` or `--all` on request).
63
+ 11. **Spreadsheet.** `build-spreadsheet.py <map>.json --out <dir>` (add `--csv` or `--csv-only`).
64
+ 12. **Gate.** Run `verify-map.py --screen-path <dir> --mapping <map>.json [--resources-root <res>]`. Exit code 2 stops publishing.
65
+ 13. **Publish.** Ask which target(s): (A) Confluence via `publish-confluence.py`, (B) a specs-repo PR under `localizations/<slug>/`, or both. Details: [publish-and-snapshot](reference/publish-and-snapshot.md).
66
+
67
+ When the Figma frame carries no annotations, say so, explain that keys will be mapped by reading the screen, and confirm the screen state (mock or screenshot) and the anchored element list with the user before rendering.
68
+
69
+ ## Choosing a verdict
70
+
71
+ | Verdict | When |
72
+ |---|---|
73
+ | `reuse` | Same function and the same or an equivalent value, or a single clean key that both legacy platforms share. The team adopts the legacy value or key and orders no new translation. |
74
+ | `review` | Same function, but the value drifted, the wording changed, the legacy key differs across platforms, or legacy covers too few languages. The team chooses. |
75
+ | `new` | Nothing in legacy corresponds to it, because the feature or the business rule is new. Both a key and a value have to be minted. |
76
+
77
+ Edge cases:
78
+
79
+ - **Component-owned key**: note `(owned by <Component>)`; do not map it twice.
80
+ - **Legacy keys differ across platforms**: keep every platform column that has a key, default to `review`.
81
+ - **`tr` equals `en`**: note it as untranslated whatever the verdict.
82
+ - **CMS present and different from `new`**: the renderer adds the warning mark; mention it in the note. CMS copy is context, not an input to the verdict.
83
+ - **Dynamic key** that cannot be resolved statically: map it with `verdict: review` and a note "dynamic - enumerate at runtime". Never drop it.
84
+ - **Partial legacy**: legacy covers only some languages (en and tr, for instance), or only one platform has a legacy key; record that in the note.
85
+ - **What reuse means**: reusing the value, or aliasing the legacy key. It is not string equality between a namespaced new key and a flat legacy key.
86
+
87
+ ## Done means
88
+
89
+ - Each row carries a `verdict`, and no Old or CMS value is made up; an empty Old or CMS cell is an acceptable state.
90
+ - `verify-map.py` passes: every `newKey` is wired (or exempt as component-owned or dynamic), no wired key is unmapped, no empty or invented values, and every `error`/`dynamic` hit from `scan-screen-keys.py` is a row.
91
+ - `build-artifact.py` ran clean; the `.md` has the header, legend, totals and the fixed column order including the CMS columns.
92
+ - The overlay PNG exists (or the `.html` fallback is noted) and the mapping's `overlay` names it.
93
+ - One keyshot PNG per matched row; the mapping's `keyshots` names the manifest; every non-dash Screenshot cell resolves to a file; `keyshots/` holds no orphans. Renaming a key or reordering rows renumbers the files, so re-render instead of renaming by hand.
94
+ - The spreadsheet has one row per mapping row and the nine fixed columns, values from `new` and annotations from `cms`, and is attached at publish.
95
+ - A second publish reports `updated` and bumps the version of the existing page id; it never creates another page.
96
+
97
+ ## References
98
+
99
+ - [sources-and-recipes](reference/sources-and-recipes.md): the origin of every column, legacy tracing on each platform, overlay and keyshot geometry, pitfalls.
100
+ - [format-and-output](reference/format-and-output.md): the mapping schema, artifact formats, spreadsheet columns and the script reference.
101
+ - [publish-and-snapshot](reference/publish-and-snapshot.md): the snapshot that keeps reads in one repo, Confluence publishing, and the specs-repo PR.
102
+ - [example mapping](example-mapping.json): a neutral mapping of nine rows that covers each edge case listed above.