{"id":52086,"plugin_id":"plugin_asdk_app_6ac8fd6e621081919d2f6f56a9043b46","kind":"skill","collection_source":"plugin_package","comparison_source":null,"observed_at":"2026-10-24T13:48:35.112Z","digest":"5db0a4d2a207631981a00a0553a25801e62227bafb52f69f82f4fa75462e8764","against":null,"payload":{"description":"Bring a wikilayer wiki, or one page in it, into translation parity with a target language. Audits cross-language coverage (missing translations, structural divergence, stale translations, possible unlinked twins, links that send a reader into another language), then translates and links the missing pages. A page target fills only that page's subtree, for a page just added or reworked. Unlike the wikilayer lint and review skills this skill writes to the wiki. Use when the user asks to translate a wiki or a single page, fill its missing translations, or check translation parity.","included_files":[],"name":"translations","skill_md_contents":"---\nname: translations\ndescription: Bring a wikilayer wiki, or one page in it, into translation parity with a target language. Audits cross-language coverage (missing translations, structural divergence, stale translations, possible unlinked twins, links that send a reader into another language), then translates and links the missing pages. A page target fills only that page's subtree, for a page just added or reworked. Unlike the wikilayer lint and review skills this skill writes to the wiki. Use when the user asks to translate a wiki or a single page, fill its missing translations, or check translation parity.\n---\n\n# wikilayer:translations\n\nParity pass across languages. lint and review only advise; this skill also writes: it creates and links the translations a wiki is missing in a target language. It audits first, fills the gaps, and reports the judgment calls for a human rather than forcing them.\n\n## Model\n\nTranslations live as parallel nodes in the same wiki, grouped by `link_translation`: a topic has one node per language, all in one group. A wiki's `primary_language` is the source; source nodes carry that language or an empty `language` field. A target-language node is a translation linked into the same group.\n\nEach language has its own front page, in the `home` role for that language; front pages pair by role rather than by a link, and their blocks are linked like any other. The rules page exists only in the primary language and is never translated.\n\nA language is a facet with a life of its own, not a mirror. A target page may hold a block or a link the source does not, and may leave out one the source has. So the skill fills what is missing only where nothing has been written yet, and a link in a target body names a target-language node.\n\nA source node is a **gap** when no node in its group carries the target language. A gap is **filled** when it is a page with no target twin, a block of a page whose target twin this run created, or a block of a page the user named as the target of a page-scoped run. On a whole-wiki run, a missing block on a target page that already existed is reported, not filled: its author may have left it out. Naming the page is the human's decision to fill it.\n\n## Procedure\n\nHold no page body in the caller: keep only ids, titles, outline rows and subagent reports, and leave reading and translating bodies to subagents.\n\n1. Resolve the target and target language from the user's request (for example \"wiki 1 to en\", or a URL plus a BCP 47 code). Resolve the wiki with `list_wikis` and take its `primary_language` as the source; never infer it from the outline. If the target or target language is unclear, ask.\n\n   **Scope.** A `wiki` target brings the whole wiki to parity; a `page` target fills only that page's subtree, for example a page just added or reworked. The map comes from the whole wiki; everything else, gaps and reports included, stays inside the scope.\n2. Read and accept every rules page the server asks for, and carry the returned signatures on every call they bind. Read them again when a call is refused for them. A translation is a write like any other, so it answers to [what a node holds](https://wikilayer.org/smee-again/wikilayer-howto/54721-pages-and-nodes) and to [wording and marks](https://wikilayer.org/smee-again/wikilayer-howto/54729-wording).\n3. Call `get_outline(<wiki-id>, max_depth=0, language=<primary>)`. If its `languages` do not include the target, or it names none, call `add_language` for the target. Without the right to, stop and say so.\n4. Read the structural map: `get_outline` at `max_depth=-1` once with `language=<primary>` and once with `language=<target>`, each through every page of results, with `fields=[\"id\",\"kind\",\"title\",\"special_role\",\"parent_id\",\"depth\",\"sort_key\",\"updated_at\"]`.\n5. Call `list_translations(wiki_id, node_ids)` over every page and block of both outlines, leaving out the wiki row and the rules page with its subtree. From the answer take:\n   - the **map** from each source node to its target twin, the front pages included through `paired_by_role`;\n   - the **gaps**: source nodes with no target twin;\n   - the **unlinked target pages**: target-language pages in no group. Most are the facet's own; they matter only as possible twins of a gap page under the same parent's twin.\n   - **stale pairs**: a target twin whose source has a later `updated_at`.\n\n   When the target outline holds no page or block but its front page, there is nothing to pair: the map is the two front pages and every other source node in scope is a gap, so skip the call.\n6. **Term list.** Before any title, settle the run's **term list**: the recurring terms of the source in scope with one target rendering each. A rendering comes from a translation dictionary the wiki or the user names, else from the target-language pages that already exist, else it is chosen once now. The list also settles how the reader is addressed where the target language makes a choice the source does not, such as a formal or informal \"you\". One subagent builds it: it reads the source pages in scope and the target pages that exist, and returns the list together with any term the target pages already render two ways. Without it, each subagent chooses its own, and one term reads differently from page to page. Hand the list to every subagent of steps 7, 8 and 9, and put the renderings chosen this run, and any term the target pages render two ways, into the report under Review.\n7. **Pages first, titles only.** Create every missing target page in scope before any body, so that every body can link a target page that exists. Go by depth; no page is created before its parent's twin. A depth of up to ten pages the caller may title itself; a larger one goes to one subagent, briefed with the source pages' ids, titles and `sort_key`s, their parents' target twins, the unlinked target pages under those twins, the term list, the target language, the wiki id and the signatures. Whoever titles a depth follows the rest of this step. Where an unlinked target page is plainly the same subject as a source page, it creates nothing for it and reports the pair. Otherwise it creates each page with `kind=page`, `language=<target>`, the translated title and the source page's `sort_key`, under the target twin of the source page's parent, or under the wiki root for a top-level source page, and returns the id pairs, target titles and URLs, which go into the map. The caller links every created pair with `link_translation(wiki_id, nodes=[{node_id, other_node_id}, …])`, up to 100 pairs a call, and leaves a reported pair for Decide: a permission check can refuse a subagent the write it lets through for the session that holds the user's request. On a page-scoped run, a source page whose parent twin is missing and outside the scope is reported as a prerequisite and not created; never fall back to the root or another convenient parent.\n8. **Then bodies.** Hand each target page in scope to one subagent, the front page included when it is in scope, where it counts as created this run when step 3 added the language; when the scope holds more than ten pages, give pages that share a parent to one subagent together, at most ten to a subagent. Run them one at a time, each after the previous one's report is read and a page of it checked: a flaw in the brief makes no subagent fail, shows only in what it wrote, and repeats in every subagent launched with it. Launch each subagent with an explicitly chosen model, and effort where the client takes one, rather than the session's: translation is judgment, so choose a strong one. Brief each, for every page it gets, with the source page id, its target twin, whether it may fill that twin (created this run, or the named page of a page-scoped run), the outline rows of the source page's blocks (id, parent, `sort_key`), the map's page pairs with their target titles and the pairs of this page's blocks, the term list, the target language, the wiki id and the signatures.\n\n   Each subagent:\n   - Reads the source page and the target twin with `get_page_markdown`, never by WebFetch of the `.md` URL, which a model can reword. The `<!-- block:N -->` comment above each node is its id; the closing `## Links here` is generated navigation: never translate it, never compare it.\n   - On a twin it may fill: writes the translated page lead where the twin has none, and on a front page created this run the translated title and lead, with `update_node`; then creates every missing block with `create_nodes`, a parent before its children, each under the target twin of its source parent and with its source `sort_key`, and a block that other blocks link before the blocks that link it, so their links can name it. On any other twin: writes nothing and lists the source blocks it has no twin of.\n   - Translates in the source's own person and mood: where the source addresses the reader or gives instructions, so does the translation, which the neutral voice of [wording](https://wikilayer.org/smee-again/wikilayer-howto/54729-wording) does not override; and where it states how a thing is, so does the translation: the sentence asserts the same about the same thing, never turned into what someone does with it or into an instruction. Weaves links onto nouns, keeps blockquotes, and mirrors the source node's title and body without summarizing or expanding. Code stays as it is, but natural-language text shown in a code block, such as a prompt the reader pastes or an example outline, is translated. Where the translation outgrows a node's length limit, it shortens wording and never drops a sentence, a link or code.\n   - Renders every term on the run's term list with the list's rendering, inflected as the sentence needs, and addresses the reader as the list says.\n   - Points every `page:N` and `block:N` it writes at the target twin: from the map, else from the blocks it created itself, else from `list_translations` on that node. `page:home` and `page:rules` stay as written. A link whose node has no twin yet keeps the source id, as when two blocks link each other or the linked block is created in the same call; step 10 repoints it.\n   - Punctuates by the target language's own conventions, never by the source's. Restraint with the em-dash is an English convention: German sets off an aside with one freely, and Russian requires one between subject and predicate when the copula is absent. Those are examples, not the list.\n   - Compares the twin's blocks with the source's and lists diverging titles and blocks out of source order. It does not list target-only blocks.\n   - Returns, for each page: the page URL; created nodes as source-to-target id pairs with URLs; the twin's id if it wrote that twin's lead or title; the renderings it chose for terms the list lacks, each with the ids of the nodes that use it; blocks it found missing or diverging; anything it did not touch, with one-line reasons.\n\n   When they return, link every created pair in batches of up to 100 with `link_translation`, as in step 7. Then reconcile those renderings: where two subagents rendered one term differently, choose one, hand the nodes that use the other to a subagent that rewrites them with `update_node`, inflecting as each sentence needs, and add every rendering chosen here, the reconciled ones included, to the report under Review.\n9. **Then proofreading, which no run skips.** A translator does not hear its own calques, so the nodes this run wrote, the pages and blocks of steps 7–8 and each lead or title step 8 wrote, go to a subagent that translated none of them, at most ten pages to a subagent, one at a time and with the model chosen as in step 8. Brief it with the source-to-target pairs of those nodes, every rendering settled this run (the term list, the renderings step 8 reconciled and those its subagents chose), the target language, the wiki id and the signatures. For each page it:\n   - reads the target page whole with `get_page_markdown` as a native reader of the target language who has not seen the source, and marks every calque in the nodes this run wrote: a phrase that keeps the source's words or construction where the target language says it otherwise, a metaphor carried over word for word, a word used in a sense the target language does not give it. Nodes this run did not write are the facet's own and stay as they are;\n   - then reads the source page, to confirm what each marked place means;\n   - rewrites each calque in the target language's own words, a body with `patch_node` and a title with `update_node`, by the rules step 8 translates by: the source's person and mood, nothing summarized or expanded, the target language's punctuation, the settled renderings, code as it is, and link targets as they are, since step 10 repoints them. A rewrite keeps what the sentence asserts and what it asserts it about, usually by keeping its subject: a statement about a thing stays one, never turned into a practice someone performs, an instruction, or a nominalization or passive the target language avoids. In Ukrainian, \"A link sits on the noun\" becomes «Посилання стоїть на іменнику», not «Посилання ставлять на іменник». Where no natural wording keeps the statement, the place stays as it is and goes into the report;\n   - returns, for each page, how many places it rewrote with a few examples, and every place it left because a rewrite would change the meaning.\n10. **Then the links left behind.** Call `search_nodes(wiki_id, language=<target>, links_across_languages=true, include_ancestors=false)`, with `subtree_id` set to the scope's target twin on a page-scoped run, through every page of results. Each hit's `cross_links` names every other-language target it links and that target's target-language `twin`, if any. In the nodes this run wrote (created in steps 7–8, plus each twin whose lead or title step 8 wrote), repoint every link that has a twin with `patch_node(wiki_id, nodes=[{node_id, replacements: [{old_target, new_target}, …]}, …])`, up to 100 nodes a call; a node the server refuses to retarget leaves the batch and goes into the report. A link without a twin stays and goes into the report. A crossing link in a node this run did not write is not rewritten, because a facet may link where it likes: it goes into the report, saying whether a twin exists.\n11. Compare child pages yourself from the two outlines: a target page under a parent other than its source page's parent's twin is a misplaced twin. A top-level source page's twin belongs under the wiki root, not under the target front page. Then aggregate everything into one markdown report, grouped by the categories below, naming every node as a link. Leave translating bodies to the subagents.\n\n## What it fills vs flags\n\nIt fills only gaps as defined above. Five situations go to a human, never resolved automatically, because each can be deliberate:\n\n1. **Structural divergence.** A source block missing from a target page it may not fill, a diverging title, a block out of source order, a misplaced page twin. Target-only nodes are a facet's own and are not reported.\n2. **Stale translation.** The source changed after its twin was written. Do not re-translate silently.\n3. **Possible unlinked twin.** An unlinked target page that is plainly the same subject as a gap page under the same parent's twin; the gap was left unfilled.\n4. **Link crossing languages.** A target body links a source-language node: either the target language has no twin of it, or an earlier translation carried the source id across although a twin exists. That facet's reader lands in another language.\n5. **Prerequisite.** A page-scoped run could not create a page because its parent's twin is missing outside the scope.\n\n## Severity\n\n- **Filled**: gaps translated and linked this run, each as a link to the new node, with how many calques proofreading rewrote on its page.\n- **Review**: structural divergence, stale translations, links crossing languages, prerequisites, the term list's new or conflicting renderings, and the places proofreading left because a rewrite would change the meaning. A human confirms whether the asymmetry is intended.\n- **Decide**: possible unlinked twins. A human decides whether to link the pair or translate the gap anyway.\n"},"changes":[],"summary":"First saved snapshot. No earlier version is available for comparison.","summary_kind":"deterministic","summary_metadata":{}}