@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.
- package/CHANGELOG.md +36 -0
- package/LICENSE +10 -0
- package/docs/facts.json +5 -4
- package/install/_dev-only-files.mjs +1 -0
- package/manifest.json +84 -82
- package/package.json +4 -3
- package/pipeline/lib/confusables.json +100 -0
- package/pipeline/lib/normalize-text.mjs +48 -0
- package/pipeline/lib/outbound-gate.mjs +10 -1
- package/pipeline/lib/redact.mjs +4 -1
- package/pipeline/lib/vercel-deploy.sh +5 -7
- package/pipeline/multi-agent-refs/features/design-conformance.md +62 -64
- package/pipeline/scripts/_notices.mjs +11 -0
- package/pipeline/scripts/agent-guard.py +30 -1
- package/pipeline/scripts/gen-skills-index.mjs +1 -1
- package/pipeline/scripts/pre-commit-check.sh +59 -16
- package/pipeline/scripts/pre-push-check.sh +3 -3
- package/pipeline/scripts/run-ui-tests.sh +5 -5
- package/pipeline/scripts/website-deploy-commit.sh +3 -2
- package/pipeline/skills/.skill-manifest.json +39 -39
- package/pipeline/skills/shared/README.md +1 -1
- package/pipeline/skills/shared/external/agent-introspection-debugging/SKILL.md +1 -0
- package/pipeline/skills/shared/external/android-architecture/SKILL.md +2 -0
- package/pipeline/skills/shared/external/android-performance/SKILL.md +2 -0
- package/pipeline/skills/shared/external/android-security/SKILL.md +2 -0
- package/pipeline/skills/shared/external/backlog/BACKLOG.md +1 -1
- package/pipeline/skills/shared/external/backlog/SKILL.md +56 -33
- package/pipeline/skills/shared/external/ci-cd-pipelines/SKILL.md +1 -0
- package/pipeline/skills/shared/external/compose-components/SKILL.md +2 -0
- package/pipeline/skills/shared/external/compose-navigation/SKILL.md +3 -2
- package/pipeline/skills/shared/external/compose-testing/SKILL.md +2 -0
- package/pipeline/skills/shared/external/council/SKILL.md +1 -0
- package/pipeline/skills/shared/external/css-modern/SKILL.md +1 -0
- package/pipeline/skills/shared/external/database-patterns/SKILL.md +1 -0
- package/pipeline/skills/shared/external/evidence-github/SKILL.md +2 -0
- package/pipeline/skills/shared/external/evidence-registry/SKILL.md +2 -0
- package/pipeline/skills/shared/external/gradle-kotlin-dsl/SKILL.md +2 -0
- package/pipeline/skills/shared/external/html-semantic/SKILL.md +1 -0
- package/pipeline/skills/shared/external/humanizer/SKILL.md +1 -0
- package/pipeline/skills/shared/external/ios-coding-standard/SKILL.md +1 -0
- package/pipeline/skills/shared/external/ios-module-structure/SKILL.md +1 -0
- package/pipeline/skills/shared/external/ios-security/SKILL.md +2 -0
- package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +91 -283
- package/pipeline/skills/shared/external/localization-reuse-map/reference/format-and-output.md +119 -151
- package/pipeline/skills/shared/external/localization-reuse-map/reference/publish-and-snapshot.md +60 -90
- package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +119 -156
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-artifact.py +726 -787
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-spreadsheet.py +253 -288
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-annotations.py +243 -304
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-legacy-labels.py +88 -104
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/publish-confluence.py +181 -235
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-key-shots.py +198 -263
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-overlay.py +461 -466
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-legacy-values.py +145 -151
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-new-values.py +123 -141
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/scan-screen-keys.py +146 -157
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/snapshot-resources.sh +22 -19
- package/pipeline/skills/shared/external/localization-reuse-map/scripts/verify-map.py +156 -140
- package/pipeline/skills/shared/external/nextjs-app-router/SKILL.md +1 -0
- package/pipeline/skills/shared/external/play-store-review/SKILL.md +2 -0
- package/pipeline/skills/shared/external/python-patterns/SKILL.md +1 -0
- package/pipeline/skills/shared/external/react-best-practices/SKILL.md +1 -0
- package/pipeline/skills/shared/external/rest-api-design/SKILL.md +1 -0
- package/pipeline/skills/shared/external/retrofit-networking/SKILL.md +2 -0
- package/pipeline/skills/shared/external/room-database/SKILL.md +2 -0
- package/pipeline/skills/shared/external/search-first/SKILL.md +1 -0
- package/pipeline/skills/shared/external/signal-community/SKILL.md +2 -0
- package/pipeline/skills/shared/external/skill-creator/SKILL.md +80 -41
- package/pipeline/skills/shared/external/skill-creator/audit.md +63 -59
- package/pipeline/skills/shared/external/skill-creator/checklist.md +28 -20
- package/pipeline/skills/shared/external/skill-creator/examples.md +40 -40
- package/pipeline/skills/shared/external/skill-creator/label-check.md +48 -36
- package/pipeline/skills/shared/external/skill-creator/scripts/audit-panel.js +91 -100
- package/pipeline/skills/shared/external/skill-creator/template.md +51 -39
- package/pipeline/skills/shared/external/tailwind-css/SKILL.md +1 -0
- package/pipeline/skills/shared/external/testing-backend/SKILL.md +1 -0
- package/pipeline/skills/shared/external/typescript-patterns/SKILL.md +1 -0
- package/pipeline/skills/shared/external/vue-composition/SKILL.md +1 -0
- package/pipeline/skills/shared/external/web-accessibility/SKILL.md +1 -0
- package/pipeline/skills/shared/external/web-performance/SKILL.md +1 -0
- package/pipeline/skills/shared/external/web-testing/SKILL.md +1 -0
|
@@ -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
|
|
@@ -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
|
---
|
|
@@ -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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
#
|
|
9
|
+
# localization-reuse-map
|
|
14
10
|
|
|
15
|
-
|
|
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
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
the
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
##
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
-
|
|
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.
|