From ad7a5bccf7d87443c6addc1a965ab52fe6b4f0d1 Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Sun, 4 Oct 2026 12:25:02 +0900 Subject: [PATCH 1/4] feat: add vendor-neutral agent skills for repository operations Add update-origin, translate-file and prh-terminology skills under .agents/skills in Agent Skills format. update-origin keeps its classification and verification scripts and per-role instruction files. --- .agents/skills/prh-terminology/SKILL.md | 152 +++++++++ .agents/skills/translate-file/SKILL.md | 66 ++++ .agents/skills/update-origin/SKILL.md | 316 ++++++++++++++++++ .../update-origin/roles/defer-md-class-d.md | 136 ++++++++ .../roles/escalate-src-class-d.md | 65 ++++ .../update-origin/roles/migrate-md-class-a.md | 77 +++++ .../update-origin/roles/migrate-md-class-b.md | 86 +++++ .../update-origin/roles/migrate-md-class-c.md | 90 +++++ .../update-origin/roles/migrate-md-roadmap.md | 72 ++++ .../roles/migrate-src-class-a.md | 77 +++++ .../roles/migrate-src-class-b.md | 76 +++++ .../roles/migrate-src-class-c.md | 80 +++++ .../update-origin/scripts/classify-md-diff.ts | 125 +++++++ .../scripts/classify-src-diff.ts | 131 ++++++++ .../scripts/verify-codeblock-sync.ts | 120 +++++++ .../update-origin/scripts/verify-migration.ts | 172 ++++++++++ 16 files changed, 1841 insertions(+) create mode 100644 .agents/skills/prh-terminology/SKILL.md create mode 100644 .agents/skills/translate-file/SKILL.md create mode 100644 .agents/skills/update-origin/SKILL.md create mode 100644 .agents/skills/update-origin/roles/defer-md-class-d.md create mode 100644 .agents/skills/update-origin/roles/escalate-src-class-d.md create mode 100644 .agents/skills/update-origin/roles/migrate-md-class-a.md create mode 100644 .agents/skills/update-origin/roles/migrate-md-class-b.md create mode 100644 .agents/skills/update-origin/roles/migrate-md-class-c.md create mode 100644 .agents/skills/update-origin/roles/migrate-md-roadmap.md create mode 100644 .agents/skills/update-origin/roles/migrate-src-class-a.md create mode 100644 .agents/skills/update-origin/roles/migrate-src-class-b.md create mode 100644 .agents/skills/update-origin/roles/migrate-src-class-c.md create mode 100644 .agents/skills/update-origin/scripts/classify-md-diff.ts create mode 100644 .agents/skills/update-origin/scripts/classify-src-diff.ts create mode 100644 .agents/skills/update-origin/scripts/verify-codeblock-sync.ts create mode 100644 .agents/skills/update-origin/scripts/verify-migration.ts diff --git a/.agents/skills/prh-terminology/SKILL.md b/.agents/skills/prh-terminology/SKILL.md new file mode 100644 index 0000000000..53063eb6ab --- /dev/null +++ b/.agents/skills/prh-terminology/SKILL.md @@ -0,0 +1,152 @@ +--- +name: prh-terminology +description: "Manage the translation terminology dictionary prh.yml for consistent Angular-ja documentation. Use before adopting a translation for a new technical term, when checking existing terminology rules, and when adding, changing, or removing rules in prh.yml." +--- + +# prh-terminology: Terminology dictionary management + +Maintain the Angular-ja project's translation terminology dictionary in `prh.yml` so that Japanese technical terms stay consistent across all Angular documentation translations. + +**Rule**: Never guess terminology. Check `prh.yml` (and apply this skill) before adopting a translation for a new technical term. Typical moments: + +- Before any translation work: check the existing rules for the terms in the file. +- For a new technical term: decide a consistent Japanese form. +- For katakana standardization: verify long vowel marks and spelling. +- After translating: add rules so the chosen form is enforced project-wide. + +## prh.yml structure overview + +The `prh.yml` file uses the prh (Proofreading Helper) format to enforce consistent terminology: + +```yaml +version: 1 +imports: + - path: ./node_modules/prh/prh-rules/files/markdown.yml +rules: + - expected: 正しい表記 # Preferred term + pattern: # Terms to replace + - 間違った表記1 + - 間違った表記2 + options: # Optional settings + wordBoundary: true # Match whole words only +``` + +## Rule categories + +### 1. 言い換え (Paraphrasing rules) + +Standardizes Japanese technical expressions. + +- "依存性の注入" (preferred) vs "依存関係の注入" (to replace) +- "変更検知" (preferred) vs "変更検出" (to replace) + +### 2. カタカナ語 (Katakana rules) + +Enforces consistent katakana spelling and English preservation. + +- **Long vowel consistency**: "サーバー" not "サーバ" +- **English preservation**: "Promise" not "プロミス" +- **Regex patterns**: `/pattern(?!suffix)/` to match specific cases + +## How to add or modify rules + +### Simple paraphrasing rule + +```yaml +- expected: 推奨される表記 + pattern: + - 置き換える表記1 + - 置き換える表記2 +``` + +### Katakana consistency rule + +```yaml +- expected: サーバー + pattern: /サーバ(?!ー)/ # Matches "サーバ" not followed by "ー" +``` + +### English preservation rule + +```yaml +- expected: Promise + pattern: プロミス +``` + +### Complex regex rule + +```yaml +- expected: ため + pattern: /(行|作)?為(?!替)/ + regexpMustEmpty: $1 # Captured group must be empty +``` + +### Word boundary rule + +```yaml +- expected: Service Worker + pattern: + - サービスワーカー + options: + wordBoundary: true # Match whole words only +``` + +## Management procedures + +### Add a new rule + +1. **Identify category**: paraphrasing or katakana. +2. **Determine the preferred form**: based on project standards. +3. **List incorrect forms**: common variations to replace. +4. **Add appropriate options**: `wordBoundary`, regex patterns. + +### Modify an existing rule + +1. **Locate the existing rule**: search by expected term. +2. **Update patterns**: add or remove incorrect forms. +3. **Adjust options**: modify regex or boundary settings. +4. **Test impact**: consider existing translations. + +### Remove a rule + +1. **Verify necessity**: confirm the rule is no longer needed. +2. **Check dependencies**: ensure no conflicts with existing translations. +3. **Document the reason**: give a clear justification for the removal. + +## Angular-specific terminology guidelines + +### Technical terms (keep in English) + +- Component, Directive, Service, Pipe +- Promise, Observable, Signal +- Router, Guard, Resolver + +### Japanese translations (standardize) + +- "依存性の注入" for Dependency Injection +- "変更検知" for Change Detection +- "遅延読み込み" for Lazy Loading + +### Katakana consistency + +- Long vowel marks: アプリケーション, サーバー, ユーザー +- Short forms: ブラウザ (not ブラウザー) + +## Validation process + +After modifying `prh.yml`: + +1. **Test with prh**: verify the syntax is valid. +2. **Run textlint** (`pnpm lint`): check integration with linting. +3. **Test on sample files**: verify the rules work correctly. +4. **Document changes**: update the rule rationale. + +## Best practices + +1. **Consistency first**: align with existing project terminology. +2. **Community input**: consider Angular-ja community preferences. +3. **Technical accuracy**: maintain precision in technical terms. +4. **Readability**: balance consistency with natural Japanese. +5. **Incremental changes**: add rules gradually to avoid disruption. + +Always test rule changes against existing translations to ensure they improve consistency without introducing errors. diff --git a/.agents/skills/translate-file/SKILL.md b/.agents/skills/translate-file/SKILL.md new file mode 100644 index 0000000000..ae57e32e73 --- /dev/null +++ b/.agents/skills/translate-file/SKILL.md @@ -0,0 +1,66 @@ +--- +name: translate-file +description: "Translate English Angular documentation file to Japanese following strict project standards. Use when translating new .en.md/.en.ts/.en.json files into Japanese with line-count preservation, anchor IDs, and terminology consistency." +--- + +Translate the file the user specifies (a path under `adev-ja/`) from English to Japanese following the angular-ja project's strict translation standards. + +You are an expert Japanese technical translator specializing in Angular documentation translation. Follow the same approach as the project's AI translation tool with two-stage processing: translation → proofreading. + +## Critical Translation Rules + +**NEVER wrap the entire text in code blocks.** Always maintain original formatting. + +**Structural Requirements:** +- **Maintain EXACT line count** - input and output must have identical number of lines +- **Preserve markdown structure absolutely** - never change heading levels, list markers, or indentation +- **Keep empty lines as empty lines** - preserve all whitespace and spacing +- **Maintain list hierarchy and markers** (*, -, +, 1., etc.) +- **Preserve code blocks unchanged** - never translate content inside code blocks +- **Keep URLs, filenames, and identifiers untranslated** +- **Preserve HTML tags and special symbols exactly** + +**AI Translation tool** +- For Markdown files, use the translator tool: `tools/translator`. + +**Heading ID Rules:** +- For headings **below h1 level** (`

` and lower), add anchor IDs based on original English heading +- Convert original heading to lowercase and remove all symbols except hyphens +- Format: `## Japanese Translation {#original-heading-lowercase}` +- Examples: + - `## How to use Angular` → `## Angularの使い方 {#how-to-use-angular}` + - `### `foo.bar`` → `### `foo.bar` {#foobar}` (remove all non-hyphen symbols) +- **h1 level headings get NO anchor IDs** + +**Special Content Rules:** +- **Never change special prefixes**: NOTE/TIP/HELPFUL/IMPORTANT/QUESTION/TLDR/CRITICAL remain in English + - Correct: `NOTE: これは重要な情報です。` + - Incorrect: `注: これは重要な情報です。` +- **No spaces around English words**: `Angularの使い方` not `Angular の使い方` +- **Preserve all Angular terminology**: component, directive, service, pipe, etc. + +**Quality Standards:** +- Follow textlint rules for Japanese technical writing +- Use appropriate katakana for foreign terms per Ministry of Education guidelines +- Maintain consistency with existing project translations +- Ensure all cross-references and internal links remain functional + +**CRITICAL: Use the `prh-terminology` skill for Terminology** +- **BEFORE translating**: Follow the `prh-terminology` skill to check existing terminology rules in `prh.yml` +- **When encountering new terms**: Follow the same skill for a consistent translation approach +- **For uncertain katakana**: Check long vowel marks and standardization with the same skill +- **After translation**: Add new terminology rules per the same skill if needed +- The skill ensures all translations follow the project's terminology dictionary + +**File Management Rules:** +- **Original content preservation**: Translated Japanese file has ALWAYS corresponding `.en.` file to preserve original content snapshot +- **Structure matching**: Translated file MUST have the same line count and document structure to the original file +- **Diff compatibility**: This enables easy comparison and tracking of changes between versions + +**Processing Approach:** +1. **Block-based translation**: Process content in heading-based blocks like the AI tool +2. **Two-stage validation**: Translate first, then apply textlint-based corrections +3. **Preserve technical accuracy** while making content accessible to Japanese developers +4. **Maintain line correspondence** for easy diff tracking between .en.md and .md files + +Always translate only the requested content, returning the translated text without additional explanations or wrapper text. diff --git a/.agents/skills/update-origin/SKILL.md b/.agents/skills/update-origin/SKILL.md new file mode 100644 index 0000000000..4cb0a441e9 --- /dev/null +++ b/.agents/skills/update-origin/SKILL.md @@ -0,0 +1,316 @@ +--- +name: update-origin +description: "Single entry point for syncing Angular upstream changes to angular-ja. Orchestrates branch creation, origin sync, diff classification, delegated migration by role, deterministic verification, per-directory commits, and PR creation. Use whenever an origin update is requested." +--- + +# update-origin: Origin Sync Orchestrator + +Single source of truth for the origin-update workflow. **Do not perform individual file migration in the orchestrating session itself.** All per-file work follows the role instruction files in `roles/`, so the orchestrating context stays small. + +## Usage + +Start the workflow with the upstream commit to sync to: + +``` +update-origin +``` + +`` is the upstream `angular/angular` commit to sync to. + +## How roles are executed + +Per-file migration instructions live in `roles/.md`. For each batch: + +- If your harness supports delegating work to sub-agents, pass the matching `roles/.md` as the instructions, together with the inputs the role lists (file paths, mode, directory), and delegate. Independent batches (for example different classes within one directory) may run in parallel. +- If it does not, follow the same `roles/.md` yourself, one batch at a time, in the order given by this document. + +Each role returns the report shape defined in its "Output" section. The orchestrator (this workflow) reads that report before verifying or committing. + +## Phase 0: Determine the correct sync target (MANDATORY) + +Never accept the user-provided `` or a tag SHA at face value. The actual production angular.dev may run a commit **after** the tag (e.g. v22.0.0 was tagged at `1cb0524f82` but production was on `fa546f382d` with `versions.json` v22 entry added in between). + +```bash +# 1. Fetch the production angular.dev footer "Built by Angular at +sha-". +# Fetch https://angular.dev/ and extract the short SHA. +# 2. Verify the short SHA resolves in the origin submodule. +# 3. If the user-supplied hash differs from the production SHA, report and confirm. +``` + +For **major-version syncs only**, also classify the new content (added markdown / config under `adev/src/content/`) and ask the user which pages to translate inside this PR. The rest go in `fix: migrate untranslated files`. + +## Phase 1: Preparation + +Run all of these in order, halting on any failure: + +```bash +# 1. Branch +git checkout -b update-origin- + +# 2. Verify current patches apply cleanly +pnpm test + +# 3. Sync origin submodule (this updates .en.* files in adev-ja) +pnpm update-origin +``` + +## Phase 2: Auto-commit + +```bash +# 2a. Origin submodule commit +git add origin +git commit -m "chore: update origin to " + +# 2b. Untranslated files commit (files with no .en counterpart) +git add $(git status --porcelain | grep -E "^\s*M.*\.(md|ts|html|json)$" | grep -v "\.en\." | cut -c4-) +# Only commit if there is anything staged. +git diff --cached --quiet || git commit -m "fix: migrate untranslated files" +``` + +### 2c. Handle upstream-deleted pages (MANDATORY: `update-origin.ts` does NOT cleanup) + +`update-origin.ts` only **copies**. Files that upstream deleted remain in `adev-ja/` as orphans and silently break navigation. List them and decide per file: + +```bash +# Enumerate translated files (.en.* exists) whose upstream counterpart is gone. +# (Same idea for untranslated .md without .en.md counterpart.) +``` + +For each orphan, **inspect the upstream deletion commit** (`git -C origin log --diff-filter=D --oneline -- `) and read its commit message **plus the additions in the same commit** to determine: + +- **Deleted = retired**: no replacement → remove from `adev-ja/` (translated `.en.*` + `.md` + untranslated `.md`) +- **Deleted = moved/merged**: replacement exists upstream → record the mapping; if the prior JA translation is salvageable as reference for the replacement, preserve the `.md` as `.md.bak` (same convention as class-d structural). Delete the `.en.md` either way (no upstream original to snapshot). + +Commit per outcome: +```bash +git commit -m "chore: remove pages deleted upstream" +git commit -m "chore: preserve removed page translations as .md.bak" # when applicable +``` + +Carry the move-target mapping forward to Phase 5.4 (PR body). + +## Phase 3: Markdown migration + +### 3.1 Classify + +```bash +pnpm exec tsx .agents/skills/update-origin/scripts/classify-md-diff.ts > /tmp/md-classification.json +``` + +Read `/tmp/md-classification.json`. **Group entries by directory** (a directory may contain multiple classes). + +### 3.2 Dispatch: directory by directory + +For each directory, run every class present **before verifying**. The verifier is directory-scoped, so leaving any class unprocessed within a directory makes verification fail. Roles for different classes within one directory may run in parallel when delegation is available. + +| Class | Role | Behavior | +| --- | --- | --- | +| a | `roles/migrate-md-class-a.md` | Verify scope, no `.md` change | +| b | `roles/migrate-md-class-b.md` | Apply small diffs preserving line correspondence | +| c | `roles/migrate-md-class-c.md` | Paragraph-level retranslation | +| d-additive | `roles/defer-md-class-d.md` (additive mode) | Restore prior translation, insert new English sections **as-is** at corresponding positions, then refresh `.en.md` | +| d-structural | `roles/defer-md-class-d.md` (structural mode) | Backup `.md` → `.md.bak`, remove `.en.md`, reset to upstream English (full defer) | + +**Class-d sub-classification (mandatory)**: Inspect the diff between old upstream English and new upstream English. + +**Default to d-additive.** Only escalate to d-structural when **headings (h2 or h3) are deleted AND the count is ≥3** OR an h2 is deleted (any count). API rename, identifier swap inside fenced code blocks, table-column header changes, prose rewording with same intent, and new section insertion are **NEVER** structural signals: they are class-a/b/c/d-additive work. Quick objective gate before defer: + +```bash +# Count deleted headings in the upstream diff. +git -C origin diff .. -- \ + | grep -E '^-(## |### )' | wc -l +``` + +If the count is <3 AND no h2 deletion, the file is d-additive. Run the class-d role in additive mode: keep the existing JA translation, insert new English sections at corresponding positions, mirror token renames in prose and tables. + +Only when h2 deletion or ≥3 h3 deletions occur, use d-structural (backup `.md` → `.md.bak`, remove `.en.md`, reset to upstream English). + +**Read the diff content, not just the +/- counts**: a "+30/-8" hunk where the 8 deletions are pure English rewording counts as additive. + +**Special-cased files (use a dedicated role regardless of class)**: + +| File | Role | Reason | +| --- | --- | --- | +| `adev-ja/src/content/reference/roadmap.md` | `roles/migrate-md-roadmap.md` | The roadmap accumulates completed work as historical records; in-progress items move to "Completed projects" rather than being replaced. The generic d roles would discard previously translated Completed entries. | + +**Do not process files in the orchestrating session itself; use the roles.** + +### 3.3 Verify (per directory, after all classes done) + +Once every class batch in the directory has reported `status: ok`, run BOTH gates in order: + +```bash +# (a) line-count + structural-marker alignment (fences/headings/annotations) +pnpm exec tsx .agents/skills/update-origin/scripts/verify-migration.ts + +# (b) code-block content sync: every diff-added line inside fenced code blocks +# must appear identically in the JA-side .md at the same line index. +pnpm exec tsx .agents/skills/update-origin/scripts/verify-codeblock-sync.ts main +``` + +Additionally, scan every changed file for stale backtick identifiers (token rename mirror leaks): inline Python equivalent until promoted to a script: + +```bash +# For each changed .en.md vs (the origin commit before this sync): +# removed_tokens = backtick-set(old.en.md) - backtick-set(new.en.md) +# leftover = removed_tokens ∩ backtick-set(.md) +# leftover MUST be empty +``` + +A non-empty `leftover` set indicates the JA side still references identifiers that upstream renamed/removed (e.g. `values` → `value`, `panelId` → `panel`, `readonly` → host-element-application). Apply the rename and re-verify. + +Both exit codes MUST be 0. If either fails, stop the workflow and report; do not commit. The codeblock check is a global scan (cheap), not a per-directory scan, so it doubles as a regression gate across all previously committed directories. + +### 3.4 Commit per class within the directory + +Even though processing is grouped per directory, **commits remain per `(class, directory)`** so git history stays granular. After verify passes, for each class present in the directory (process in alphabetical order: a → b → c → d): + +**Commit messages are a public surface. They must describe what changed in the documentation, never the internal class letter, agent name, or script name** (see "Public-surface hygiene" below). Use the message that matches the class, with the class itself left unstated: + +```bash +# Stage only the files belonging to that class within this directory +git add + +# class a +git commit -m "fix(docs): mirror upstream code and link changes in " +# class b +git commit -m "fix(docs): apply upstream wording changes in " +# class c +git commit -m "fix(docs): retranslate updated sections in " +# class d, additive mode +git commit -m "chore(docs): insert new upstream sections untranslated in " +# class d, structural mode +git commit -m "chore(docs): reset rewritten pages to upstream English in " +``` + +Repeat 3.2 → 3.4 for each directory until all markdown batches are processed. + +## Phase 4: Source-code migration + +### 4.1 Classify + +```bash +pnpm exec tsx .agents/skills/update-origin/scripts/classify-src-diff.ts > /tmp/src-classification.json +``` + +**Group entries by directory** (same rationale as Phase 3.1). + +### 4.2 Dispatch: directory by directory + +For each directory, run every class present before verifying. + +| Class | Role | Behavior | +| --- | --- | --- | +| a | `roles/migrate-src-class-a.md` | Mirror code-only changes | +| b | `roles/migrate-src-class-b.md` | Apply ≤5 translatable token changes | +| c | `roles/migrate-src-class-c.md` | Structural + multi-token changes | +| d | `roles/escalate-src-class-d.md` | Escalate to user: do NOT auto-modify | + +### 4.3 Verify (per directory) + +```bash +pnpm exec tsx .agents/skills/update-origin/scripts/verify-migration.ts +``` + +### 4.4 Commit per class within the directory + +After verify passes, for each class present (a → b → c, **excluding d**). Same public-surface rule as Phase 3.4: the class letter never appears in the message. + +```bash +git add +# class a +git commit -m "fix(app): mirror upstream code changes in " +# class b / c +git commit -m "fix(app): apply upstream changes to translated strings in " +``` + +For class-d escalations, **stop and report to user**. Do not commit class-d files until the user resolves them. + +## Phase 5: Final verification & PR + +```bash +# 5.1 Patch / lint full pipeline +pnpm test +pnpm lint + +# 5.2 Working tree must be clean +git status --porcelain # must be empty + +# 5.3 Angular.jp-specific configuration sanity check +grep -n "indexName" build/src/environments/environment.ts || true # if applicable +# Confirm fixed values per "Angular.jp specific configuration rules" in `AGENTS.md` + +# 5.4 Push to your own fork and create the PR against angular/angular-ja. +# Check `git remote -v` first and use the remote that points to your fork. +git push -u update-origin- +gh pr create --repo angular/angular-ja --base main --head :update-origin- \ + --title "chore: update origin to " \ + --body "$(cat <<'EOF' +## Summary +Sync angular-ja with upstream angular/angular @ . + +## Scope +- N markdown files and M source files updated against the new upstream English. +- Notable content changes: describe them by what a reader of the site would notice, page by page. + +## Removed / moved pages (mandatory section) +For each upstream-deleted file, list: prior JA path → move target upstream path (or "retired"). For preserved `.md.bak`, note their intended re-use (reference for new page translation or archive only). If upstream deleted nothing, say so explicitly. + +## Untranslated / deferred (mandatory section) +- New pages copied untranslated this PR: list +- Pages reset to upstream English with the prior translation kept as `.md.bak`: list, with the upstream reason per page +- Pages where new upstream sections were inserted untranslated in place: list + +## Test plan +- [x] pnpm test +- [x] pnpm lint +- [x] Line-count and structural-marker parity checked for all `.en.*` / translated pairs +- [x] Fenced code-block content verified identical between English and Japanese for all changed files +- [x] No backtick identifier removed upstream remains on the Japanese side +EOF +)" +``` + +Note: the PR is created **from `:` to `angular/angular-ja:main`** (fork to upstream). The `--repo angular/angular-ja --head :` form is required; the bare `--head ` form fails with "No commits between main and ". + +## Rules (apply throughout) + +- **Never** process per-file migration in the orchestrating session itself. Always go through the roles (delegated, or followed one by one when delegation is unavailable). +- **Never** combine multiple `(class, directory)` batches into one commit. +- **Never** verify until all classes in the directory have been processed (the verifier is directory-scoped). +- **Never** skip `verify-migration.ts`. Its exit code is the gate. +- **Never** edit `.patch` files manually. Use the Safe Patch Update Procedure if patches fail. +- **Always** match `wc -l ` exactly for markdown: no exceptions. +- **Always** preserve **line-prefix** docs annotations `IMPORTANT:`, `NOTE:`, `HELPFUL:`, `TIP:`, `CRITICAL:`, `WARNING:`, `TLDR:` literally in uppercase English when they appear at the start of a line (optionally after blockquote `>`). Never lowercase or translate them (no `Tip:`, `重要:`, `警告:`, `ヒント:`). The adev build pipeline relies on these uppercase line-leading markers. The same words appearing inside heading text or mid-sentence are normal prose and may be translated. +- **Always** stop and report on any role report with `status: needs_review` / `aborted` / `escalate`. + +## Public-surface hygiene (absolute) + +Commit messages, PR titles, and PR bodies are read by upstream maintainers and are permanent in GitHub's history. They must contain **nothing** from this workflow's internal tooling. The following vocabulary is forbidden on those surfaces: + +- Class letters and their labels: `class-a`, `md-class-b`, `src-class-c`, `class-d`, `d-additive`, `d-structural`, `md-roadmap` +- Script names: `classify-md-diff.ts`, `classify-src-diff.ts`, `verify-migration.ts`, `verify-codeblock-sync.ts` +- Role names: `migrate-md-class-*`, `defer-md-class-d`, `escalate-src-class-d`, `migrate-src-class-*`, `migrate-md-roadmap`, `prh-terminology` +- Any `.agents/` path, any skill or role name, and any absolute home path +- The word "orchestrator" and other descriptions of the internal agent topology + +Describe the work by what changed in the documentation and how it was checked, not by which internal machinery produced it. "Line-count and structural-marker parity checked for all pairs" is publishable; "verify-migration.ts exits 0" is not. + +Every commit message this workflow writes is subject to this rule. Applying it after the fact requires rewriting history on a pushed branch, which leaves the original text reachable through the PR's force-push timeline, so the leak is never fully undone. Get it right on the first commit. + +## Prose style (applies to PR bodies and any Japanese output) + +- Never use an em dash (`—`). Use a colon, parentheses, or a separate sentence. +- Avoid the punctuation habits that mark machine-generated text: em-dash asides, "not X, but Y" parallelism as a tic, and decorative arrows in prose. + +## Scripts (in `scripts/` of this skill) + +- `scripts/classify-md-diff.ts`: markdown diff classifier +- `scripts/classify-src-diff.ts`: source-code diff classifier +- `scripts/verify-migration.ts`: deterministic verifier (presence + line count + structural-marker alignment) +- `scripts/verify-codeblock-sync.ts`: verifies that every diff-added line inside fenced code blocks is mirrored to the JA-side `.md` + +## Roles (in `roles/` of this skill) + +- `roles/migrate-md-class-{a,b,c}.md`, `roles/defer-md-class-d.md`, `roles/migrate-md-roadmap.md` +- `roles/migrate-src-class-{a,b,c}.md`, `roles/escalate-src-class-d.md` diff --git a/.agents/skills/update-origin/roles/defer-md-class-d.md b/.agents/skills/update-origin/roles/defer-md-class-d.md new file mode 100644 index 0000000000..f58c7b9189 --- /dev/null +++ b/.agents/skills/update-origin/roles/defer-md-class-d.md @@ -0,0 +1,136 @@ +# Role: defer-md-class-d + +Handle class-d markdown files during Phase 3 of the update-origin workflow. The orchestrator chooses one of two modes per file based on the actual content of the upstream diff. + +## Mode selection (assigned by orchestrator) + +The orchestrator inspects `git show main: | diff - ` and tells you which mode to run: + +**Default mode is d-additive.** Only escalate to d-structural under the objective gate below. + +- **`d-additive`** (default): Existing Japanese translation must survive; new English sections are inserted as-is at corresponding positions and flagged for follow-up translation. Token renames (`values` → `value`, `panelId` → `panel`, etc.) in prose and tables MUST be mirrored to JA on the same line. This is normal additive work, not a structural signal. +- **`d-structural`**: Reserved for cases where the page has been so heavily restructured that the prior translation is unsalvageable. **Objective gate**: at least one h2 deletion OR ≥3 h3 deletions in the upstream diff. Count with: + + ```bash + git -C origin diff cea6588bb3.. -- adev/.md | grep -E '^-(## |### )' | wc -l + ``` + + API rename, identifier swap, table-column changes, prose rewording, and new section insertion are **NEVER** structural signals by themselves. If the gate is not met, run additive even if you feel uncertain. + +## Input + +- A list of `.en.md` file paths under one directory. +- A mode (`d-additive` or `d-structural`). + +The post-`pnpm update-origin` state is: `.en.md` already reflects new upstream English; `.md` still holds the old Japanese translation. + +--- + +## Mode A: `d-additive` + +**Goal**: keep the existing Japanese translation; only insert the new English blocks (untranslated) at the corresponding positions, and refresh `.en.md`. + +### Procedure (per file) + +1. **Compute the additive diff** between old upstream English and new upstream English: + ```bash + git show main: > /tmp/old.en.md + diff /tmp/old.en.md + ``` + Each hunk should be either pure additions (`>` lines only) or near-additive (≤2 `<` lines whose deletion does not affect the corresponding Japanese meaning). + +2. **Read the current `.md`** (existing Japanese translation, line-aligned with the OLD `.en.md`). + +3. **For each additive hunk**, find the anchor in the existing `.md` (use the surrounding unchanged context lines from the diff, which are the same in old `.en.md` and current `.md` line indices) and **insert the new English block as-is** at that position. Do NOT translate inserted blocks; that is a follow-up. + +4. **For near-additive hunks** (English rewording that does not change Japanese meaning), leave the existing Japanese line untouched. Do not replace working translations with English. + +5. **Verify line count parity** with the new `.en.md`: + ```bash + wc -l + ``` + Counts MUST be identical. + +6. **Verify structural alignment** (fenced code fences, headings, line-prefix annotations) by running: + ```bash + pnpm exec tsx .agents/skills/update-origin/scripts/verify-migration.ts + ``` + Exit code MUST be 0. + +### Output (additive) + +```json +{ + "class": "d", + "mode": "d-additive", + "directory": "", + "files": [ + { + "file": "", + "status": "ok" | "needs_review", + "inserted_blocks": [""] + } + ] +} +``` + +The orchestrator commits the result (message per Phase 3.4 of `SKILL.md`). Additive blocks are deferred translation work, just embedded in place rather than as `.md.bak`. + +--- + +## Mode B: `d-structural` + +**Goal**: Full defer. Reset `.md` to upstream English; preserve prior Japanese as `.md.bak` for human re-translation reference. + +### Procedure (per file) + +1. **Back up the existing Japanese translation**: + ```bash + mv + ``` + +2. **Delete the `.en.md`** (the new English source will replace it as plain English): + ```bash + rm + ``` + +3. **Restore the upstream English file at the translated-file path** (untranslated state): + ```bash + cp origin/adev//.md + ``` + Use the path relative to `origin/adev/` corresponding to the file under `adev-ja/`. + + Result: `` is now plain English (matching upstream), `` holds the previous Japanese translation for future re-translation, and `` is absent, meaning "untranslated" by project convention. + +4. **Verify**: + - `` exists and matches `origin/adev/.../.md` byte-for-byte. + - `` exists. + - `` does NOT exist. + +### Output (structural) + +```json +{ + "class": "d", + "mode": "d-structural", + "directory": "", + "files": [ + { + "file": "", + "backup": ".bak", + "status": "deferred" + } + ] +} +``` + +The orchestrator commits the result (message per Phase 3.4 of `SKILL.md`). + +--- + +## Rules (apply to both modes) + +- Do **not** translate any inserted English in this role. Translation is a follow-up. +- Do **not** modify untouched Japanese content. +- In `d-additive`, never silently switch to `d-structural` mid-file. If a hunk turns out to be non-additive, abort with `status: needs_review` and report the hunk; the orchestrator will re-route as `d-structural`. +- Do **not** commit. The orchestrator commits per directory. diff --git a/.agents/skills/update-origin/roles/escalate-src-class-d.md b/.agents/skills/update-origin/roles/escalate-src-class-d.md new file mode 100644 index 0000000000..eff1c8c1b0 --- /dev/null +++ b/.agents/skills/update-origin/roles/escalate-src-class-d.md @@ -0,0 +1,65 @@ +# Role: escalate-src-class-d + +Escalate class-d source-code files (full rewrite, fundamental restructure) to user judgment. Source code cannot be reset to an untranslated state like markdown, so a human decision is required. Used in Phase 4 of the update-origin workflow. + +## Scope + +Class-d for source code means the file is fundamentally restructured: + +- The file is effectively a different file now +- More than 100 total line changes with high translatable-token density +- Auto-migration would risk breaking navigation / templates / app behavior + +Unlike markdown class-d, source-code files **cannot** be reset to "untranslated" state: they must remain functional. So this role **does NOT auto-defer**. It collects context and **escalates to the user**. + +## Input + +A list of `.en.{ts,html,json}` file paths. + +## Procedure + +For each `.en.*` file: + +1. **Capture diff and current state**: + ```bash + git diff HEAD -- > /tmp/.diff + wc -l + ``` + +2. **Summarize**: + - File path + - Diff size (lines added / removed) + - Translatable token density estimate + - Risk areas (e.g., "removed 12 navigation entries that have Japanese translations", "wholesale template rewrite") + +3. **Leave the working tree as is**: keep both `.en.*` and translated file as currently checked out by `pnpm update-origin`. Do NOT commit, do NOT translate, do NOT delete. + +## Completion Conditions + +- Working tree is unchanged for these files (still showing the modified state). +- A consolidated escalation report is produced. + +## Output + +```json +{ + "class": "d", + "kind": "src", + "status": "escalate", + "files": [ + { + "file": "", + "diffSize": { "added": , "removed": }, + "risk": "", + "recommendedAction": "manual-review" | "split-into-smaller-pr" | "defer-to-followup-pr" + } + ], + "message": "These source-code files require human review before the origin sync can complete. Proposed actions inline." +} +``` + +## Rules + +- Do **not** translate, edit, or revert anything. +- Do **not** commit. +- Surface the escalation report to the orchestrator. The orchestrator pauses Phase 4 until the user resolves these files manually or instructs how to proceed. diff --git a/.agents/skills/update-origin/roles/migrate-md-class-a.md b/.agents/skills/update-origin/roles/migrate-md-class-a.md new file mode 100644 index 0000000000..270b8cd991 --- /dev/null +++ b/.agents/skills/update-origin/roles/migrate-md-class-a.md @@ -0,0 +1,77 @@ +# Role: migrate-md-class-a + +Apply class-a markdown migration: low-risk changes that nevertheless may need to be mirrored to the Japanese `.md` (code in fenced blocks, URLs, prose identifiers). Used in Phase 3 of the update-origin workflow for batches classified as class a by `classify-md-diff.ts`. + +## Scope + +Class-a means the diff is **small and structurally simple**, but it is NOT automatically a no-op for the Japanese side. You MUST mirror three categories of change to the `.md`: + +1. **Code inside fenced code blocks**: the code is shared verbatim with the upstream English; if upstream renames an import, identifier, or value, the Japanese-side fenced block must be updated to the same code. +2. **URLs / link paths in markdown links**: the `](path)` value must match upstream exactly (e.g. `(guide/components)` → `(/guide/components)`). +3. **Code identifiers in prose backticks**: when a backtick token in prose changes (e.g. `item.key` → `item.value`, `getHarnesses` → `getAllHarnesses`), the Japanese prose on the same line must adopt the new token. + +The Japanese `.md` only stays byte-identical when ALL of the following hold: + +- The diff is purely an English-prose typo (e.g. `manubar` → `menubar`, `exisiting` → `existing`) where the Japanese translation already conveyed the correct meaning naturally. +- No code inside fenced blocks changed. +- No URL / link path changed. +- No backtick identifier in prose changed. + +## Input + +A list of `.en.md` file paths (already updated on disk by `pnpm update-origin`). All files share one directory. + +## Procedure + +1. **Inspect the diff**: For each `.en.md`, run: + ```bash + git diff HEAD -- + ``` + Walk EVERY hunk and classify each change: + - (a) Fenced code block content change → mirror to `.md`. + - (b) URL / link path change → mirror to `.md`. + - (c) Backtick identifier change in prose → mirror to `.md` on the same line (Japanese prose around it stays). + - (d) Pure English-prose typo with no semantic identifier change → leave `.md` unchanged. + - (e) Anything else (semantic prose change, structural change) → abort and report; this is not class-a. + +2. **Apply mirroring edits to `.md`** for any (a), (b), (c) changes from step 1. Each edit must be on the SAME line index as the corresponding change in `.en.md` (line correspondence is preserved). Do NOT translate; just replace the changed token / URL / code while keeping the surrounding Japanese prose intact. + +3. **Verify line count parity**: + ```bash + wc -l + ``` + Counts MUST be identical. + +4. **Per-line spot check**: For each line touched in `.en.md`, read the same line index in `.md` and confirm: + - All backtick code tokens that appear in `.en.md`'s line also appear in `.md`'s line (substring match). + - All `](url)` link paths in `.en.md`'s line also appear in `.md`'s line. + - Any fenced code block content matches verbatim between sides. + + If a token / URL is missing on the JA side, fix it before completing. + +## Completion Conditions (all required) + +- Every (a), (b), (c) hunk has been mirrored to the corresponding `.md` line. +- `wc -l` matches between every `.en.md` and `.md`. +- Per-line spot check passes for all changed lines. + +## Output + +Return a JSON-shaped summary: + +```json +{ + "class": "a", + "directory": "", + "files": ["", ...], + "status": "ok" | "aborted", + "reason": "" +} +``` + +## Rules + +- Do **not** translate. Class-a only mirrors code / URL / identifier-token changes; surrounding Japanese prose stays. +- Do **not** rewrite or restructure paragraphs. If the diff requires that, abort with `status: aborted` and report; the orchestrator will reclassify. +- Do **not** run `pnpm lint --fix` here. +- Do **not** commit. The orchestrator commits per directory. diff --git a/.agents/skills/update-origin/roles/migrate-md-class-b.md b/.agents/skills/update-origin/roles/migrate-md-class-b.md new file mode 100644 index 0000000000..aa69471d15 --- /dev/null +++ b/.agents/skills/update-origin/roles/migrate-md-class-b.md @@ -0,0 +1,86 @@ +# Role: migrate-md-class-b + +Apply class-b markdown migration: small diffs (≤ 5 prose lines, no paragraph restructure). Used in Phase 3 of the update-origin workflow for batches classified as class b by `classify-md-diff.ts`. + +## Scope + +Class-b means the `.en.md` diff is small and localized: + +- Up to 5 prose lines changed (added/removed/modified) +- No paragraph-level restructuring +- Sentence-level rewording, supplementary phrases, link/text additions + +The corresponding `.md` file needs **partial diff application** preserving line-by-line correspondence with `.en.md`. + +## Input + +A list of `.en.md` file paths in a single directory. + +## Procedure + +For each `.en.md` file: + +1. **Read the diff**: + ```bash + git diff HEAD -- + ``` + +2. **Read the .en.md source** (post-update-origin state). This is the authoritative English text. + +3. **Read the corresponding `.md` file**. + +4. **Apply parallel changes to `.md`**: + - For each changed hunk in `.en.md`, locate the same line range in `.md`. + - Translate the changed English content to Japanese (follow the `prh-terminology` skill for terminology if uncertain). + - Preserve unchanged surrounding lines exactly. + - Maintain line-by-line correspondence: every `.en.md` line index must have a matching `.md` line index. + - **CRITICAL: Preserve special-prefix annotations at line start.** When a line begins with one of `IMPORTANT:`, `NOTE:`, `HELPFUL:`, `TIP:`, `CRITICAL:`, `WARNING:`, `TLDR:` (optionally after blockquote `>` markers), it is a docs annotation recognized by the adev build pipeline. The prefix MUST stay literally in uppercase English (e.g. `IMPORTANT: `; `Tip:` is wrong, `重要:` is wrong, `警告:` is wrong). Translate only the body after the colon. This rule applies ONLY to line-leading prefixes; the same words appearing inside headings or mid-sentence are normal text and may be translated naturally. + +5. **Verify line count parity**: + ```bash + wc -l + ``` + Counts MUST be identical. If different, fix immediately. + +6. **Run lint on this file**: + ```bash + pnpm lint --fix + pnpm lint + ``` + Both must succeed. + +After processing all files: + +7. **Run migration verifier on the directory**: + ```bash + pnpm exec tsx .agents/skills/update-origin/scripts/verify-migration.ts + ``` + Exit code must be 0. + +## Completion Conditions (all required) + +- Every `.en.md` and `.md` pair has identical line count. +- Every changed hunk has a corresponding Japanese-translated change in `.md`. +- `pnpm lint` passes for all modified `.md`. +- `verify-migration.ts ` exits 0. + +## Output + +```json +{ + "class": "b", + "directory": "", + "files": ["", ...], + "status": "ok" | "needs_review", + "issues": [{ "file": "", "issue": "" }] +} +``` + +If line counts cannot be reconciled or translation requires paragraph-level rewrite, set status to `needs_review` and report. Do **not** silently escalate to class-c behavior. + +## Rules + +- Follow the `prh-terminology` skill for any uncertain terminology. +- Apply the project translation conventions (see "Translation rules" in `AGENTS.md`). +- Do **not** rewrite untouched paragraphs. Class-b is strictly localized changes. +- Do **not** commit. The orchestrator commits per directory. diff --git a/.agents/skills/update-origin/roles/migrate-md-class-c.md b/.agents/skills/update-origin/roles/migrate-md-class-c.md new file mode 100644 index 0000000000..04f1c23d04 --- /dev/null +++ b/.agents/skills/update-origin/roles/migrate-md-class-c.md @@ -0,0 +1,90 @@ +# Role: migrate-md-class-c + +Apply class-c markdown migration: paragraph-level retranslation (≤ 30 prose lines, structural change within file). Used in Phase 3 of the update-origin workflow for batches classified as class c by `classify-md-diff.ts`. + +## Scope + +Class-c means the `.en.md` diff requires paragraph-level retranslation: + +- 6–30 prose lines changed +- Paragraph rewriting, multiple sentence rewordings, structural reordering within the file +- Page identity preserved (still the same topic / chapter) + +The corresponding `.md` file needs **paragraph-level retranslation** preserving heading IDs and line-by-line correspondence with `.en.md`. + +## Input + +A list of `.en.md` file paths in a single directory. + +## Procedure + +For each `.en.md` file: + +1. **Read the full diff** to understand intent: + ```bash + git diff HEAD -- + ``` + +2. **Read both `.en.md` (post-update) and `.md` in full** to ground retranslation in context. + +3. **Identify affected paragraphs** in `.en.md` (group changed hunks by paragraph). + +4. **Retranslate each affected paragraph**: + - Translate the new English paragraph to Japanese. + - Follow the `prh-terminology` skill for terminology consistency. + - Apply the project translation rules (see "Translation rules" in `AGENTS.md`): + - Italic: `*text*` not `_text_` + - Standardized terminology: 基本ガイド, 真の情報源, 即座に, 最新情報, etc. + - Heading IDs: `{#kebab-case}` on `##` and `###` headings + - **CRITICAL: Preserve special-prefix annotations at line start.** When a line begins with one of `IMPORTANT:`, `NOTE:`, `HELPFUL:`, `TIP:`, `CRITICAL:`, `WARNING:`, `TLDR:` (optionally after blockquote `>` markers), it is a docs annotation recognized by the adev build pipeline. The prefix MUST stay literally in uppercase English (e.g. `IMPORTANT: `; `Tip:` is wrong, `重要:` is wrong, `警告:` is wrong). Translate only the body after the colon. This rule applies ONLY to line-leading prefixes; the same words appearing inside headings or mid-sentence are normal text and may be translated naturally. + - Preserve untouched paragraphs verbatim. + - Maintain line-by-line correspondence with `.en.md`. + +5. **Verify line count parity**: + ```bash + wc -l + ``` + Counts MUST be identical. + +6. **Run lint on this file**: + ```bash + pnpm lint --fix + pnpm lint + ``` + +After processing all files: + +7. **Run migration verifier on the directory**: + ```bash + pnpm exec tsx .agents/skills/update-origin/scripts/verify-migration.ts + ``` + Exit code must be 0. + +## Completion Conditions (all required) + +- Every `.en.md` and `.md` pair has identical line count. +- All paragraph-level changes in `.en.md` are reflected in `.md` with quality Japanese translation. +- Heading IDs are preserved or added per project convention. +- `pnpm lint` passes for all modified `.md`. +- `verify-migration.ts ` exits 0. + +## Output + +```json +{ + "class": "c", + "directory": "", + "files": ["", ...], + "status": "ok" | "needs_review", + "issues": [{ "file": "", "issue": "" }] +} +``` + +If the change scope expanded beyond class-c during analysis (e.g., page identity changed, > 50% of file rewritten), set status to `needs_review` with reason. Do **not** silently proceed as if it were class-c. The orchestrator will route it to the class-d defer flow. + +## Rules + +- Always read the source `.en.md` before translating. Never modify `.md` based on "what sounds natural" alone. +- Follow the `prh-terminology` skill for terminology decisions. +- Preserve `.md` content for unchanged paragraphs verbatim. Do not opportunistically refactor. +- Do **not** commit. The orchestrator commits per directory. diff --git a/.agents/skills/update-origin/roles/migrate-md-roadmap.md b/.agents/skills/update-origin/roles/migrate-md-roadmap.md new file mode 100644 index 0000000000..c5d8c80705 --- /dev/null +++ b/.agents/skills/update-origin/roles/migrate-md-roadmap.md @@ -0,0 +1,72 @@ +# Role: migrate-md-roadmap + +Specialized migration handler for `adev-ja/src/content/reference/roadmap.md`. The roadmap evolves by accumulating completed work as historical records, so it cannot be migrated by the generic class-a/b/c/d roles. Used in Phase 3 of the update-origin workflow whenever `roadmap.md` appears in the classifier output. + +## Why roadmap.md is special + +Each Angular release reshapes the roadmap by **moving in-progress items to "Completed projects"** as historical records, **adding new items**, and **occasionally deleting outdated items**. The previous Japanese translation must be preserved wherever possible: Completed projects already translated should remain in Japanese as historical documentation, even when the upstream restructured the surrounding sections. + +## Inputs + +- `adev-ja/src/content/reference/roadmap.md`: current state: upstream English (defer applied by the orchestrator before this role runs). +- `adev-ja/src/content/reference/roadmap.md.bak`: previous Japanese translation. +- Old upstream English: `git show main:adev-ja/src/content/reference/roadmap.en.md`. +- New upstream English: same as current `.md`. + +## Hunk Classification (mandatory) + +Walk every hunk in `git show main:adev-ja/src/content/reference/roadmap.en.md | diff - adev-ja/src/content/reference/roadmap.md` and classify each: + +| Pattern | Action | +| --- | --- | +| **Pure addition in In-progress / Available / Future sections** (new `` or list entry, no removal) | Insert as English-as-is at the corresponding section; flag for follow-up translation | +| **`` removed from in-progress AND added to "Completed projects" with `link="Completed in ..."`** | Move the **Japanese-translated** `` block (from `.md.bak`) to the "Completed projects" section; do NOT replace with English. Adjust the heading's link attribute to match upstream's `Completed in ...` text. | +| **In-progress `` body rewritten without moving to Completed** (same title, different prose / additional context) | Keep the previous Japanese title and surrounding structure; replace the body prose with the new English as-is. Translation is a follow-up. | +| **Whole section removed** (e.g. the "Future work, explorations, and prototyping" header + its body) | Delete the corresponding Japanese section entirely from the working copy (it is no longer in upstream). | +| **Whole new section added** (new H2 with content) | Insert the new section in English as-is. | +| **Goals list expanded** (e.g. "two goals" → "three goals", with new bullet) | Update the count word and add the new bullet in English at the corresponding position. | +| **Stylistic English-only edit** (e.g. removing trailing `href=""` attribute, list markers `1.` vs `2.`) | Mirror the change so line correspondence stays exact; the visible Japanese prose around it remains. | + +## Procedure + +1. Verify the inputs exist (`.md`, `.md.bak`, plus `git show main:...en.md` succeeds). If not, abort. +2. Compute the diff and classify every hunk per the table above. Build an internal plan listing each hunk and its action. +3. Construct the result `.md` starting from `.md.bak` and applying the planned actions in upstream-line order: + - Preserve every Japanese sentence that has not been touched by upstream. + - Move Completed-projects entries from their old position to the new Completed-projects section, **keeping the Japanese text**. + - Insert / replace English sections per the classification. +4. Recreate `.en.md` by copying `origin/adev/src/content/reference/roadmap.md`. +5. Delete `.md.bak`. +6. Verify `wc -l ` are identical. +7. Per-line spot check: each hunk should have produced a `.md` line-aligned with the new `.en.md`. +8. Run `pnpm exec tsx .agents/skills/update-origin/scripts/verify-migration.ts adev-ja/src/content/reference`. + +## Completion Conditions (all required) + +- Every hunk has been classified and applied. +- Completed-projects entries that were merely moved (not rewritten) retain their previous Japanese translation. +- New / replaced sections are inserted in English as-is, line-aligned. +- Removed sections are gone. +- `wc -l` parity holds. +- `.md.bak` is deleted; `.en.md` is regenerated from upstream. + +## Output + +```json +{ + "file": "adev-ja/src/content/reference/roadmap.md", + "status": "ok" | "needs_review" | "aborted", + "hunks": [ + { "kind": "added-in-progress" | "moved-to-completed" | "rewritten-in-place" | "section-removed" | "section-added" | "goal-list-update" | "stylistic", "summary": "...", "translation_preserved": true | false } + ], + "lines": , + "follow_up_translation_needed": [""] +} +``` + +## Rules + +- **Never** wholesale-replace the file with English (the generic d-structural defer). Roadmap accumulates history; previous Completed translations must survive. +- **Never** translate inserted English (in-progress additions). That is a follow-up PR. +- **Always** preserve Japanese for items that were merely repositioned (in-progress → Completed) without prose rewrite. +- **Do not** commit; the orchestrator commits per directory. diff --git a/.agents/skills/update-origin/roles/migrate-src-class-a.md b/.agents/skills/update-origin/roles/migrate-src-class-a.md new file mode 100644 index 0000000000..02b8a32abb --- /dev/null +++ b/.agents/skills/update-origin/roles/migrate-src-class-a.md @@ -0,0 +1,77 @@ +# Role: migrate-src-class-a + +Apply class-a source-code migration: changes that do NOT affect translatable tokens (logic / type / structure only). For `.ts` / `.html` / `.json` files. Used in Phase 4 of the update-origin workflow. + +## Scope + +Class-a means the diff in `.en.{ts,html,json}` does **not** touch translatable text: + +- Pure code-logic / type / import changes +- Non-text attribute changes in HTML +- Schema-only JSON changes (no user-facing strings) + +The corresponding `.{ts,html,json}` should follow the same code-level changes but its translated strings remain untouched. + +## Input + +A list of `.en.{ts,html,json}` file paths in a single directory. + +## Procedure + +For each `.en.*` file: + +1. **Read the diff**: + ```bash + git diff HEAD -- + ``` + Confirm no translatable token (label/title/description/text-node/JSON user-facing string) is affected. If any is, **abort and report**. + +2. **Apply the same code-level changes to the translated file** while preserving Japanese strings: + - For `.ts`: mirror import / type / logic changes, keep `label:` / `title:` etc. values in Japanese. + - For `.html`: mirror tag / attribute / directive changes, keep text nodes in Japanese. + - For `.json`: mirror non-string-value changes, keep translated string values intact. + +3. **Verify the translated file is syntactically valid**: + - `.ts` / `.html`: rely on `pnpm lint`. + - `.json`: ensure `JSON.parse` succeeds (project test step covers this). + +4. **Run lint on this file**: + ```bash + pnpm lint --fix + pnpm lint + ``` + +After processing all files: + +5. **Run migration verifier on the directory**: + ```bash + pnpm exec tsx .agents/skills/update-origin/scripts/verify-migration.ts + ``` + Exit code must be 0. + +## Completion Conditions + +- Every `.en.*` and translated pair exists. +- Code-level changes mirrored in translated file. +- No Japanese translation strings altered. +- `pnpm lint` passes. +- `verify-migration.ts` exits 0. + +## Output + +```json +{ + "class": "a", + "kind": "src", + "directory": "", + "files": ["", ...], + "status": "ok" | "aborted", + "reason": "" +} +``` + +## Rules + +- Do **not** alter translated user-facing strings. +- Source code line counts are NOT enforced (unlike markdown). +- Do **not** commit. The orchestrator commits per directory. diff --git a/.agents/skills/update-origin/roles/migrate-src-class-b.md b/.agents/skills/update-origin/roles/migrate-src-class-b.md new file mode 100644 index 0000000000..fadb519335 --- /dev/null +++ b/.agents/skills/update-origin/roles/migrate-src-class-b.md @@ -0,0 +1,76 @@ +# Role: migrate-src-class-b + +Apply class-b source-code migration: ≤ 5 translatable token changes in `.ts` / `.html` / `.json`. Used in Phase 4 of the update-origin workflow. + +## Scope + +Class-b means the `.en.{ts,html,json}` diff includes a small number of translatable-token changes: + +- Up to 5 lines that contain translatable strings (`label:`, `title:`, text nodes, user-facing JSON values) added / changed +- No structural reorganization + +## Input + +A list of `.en.{ts,html,json}` file paths in a single directory. + +## Procedure + +For each `.en.*` file: + +1. **Read the diff**: + ```bash + git diff HEAD -- + ``` + +2. **Identify translatable changes**: + - `.ts`: `label`, `title`, `description`, `message`, `summary`, `placeholder`, `tooltip`, `action` + - `.html`: text nodes, `aria-label`, `title=""`, `alt=""` + - `.json`: user-facing string values + +3. **Apply the changes to the translated file**: + - Mirror non-translatable changes (paths, identifiers, types, directives) verbatim. + - Translate new / changed user-facing strings to Japanese (follow the `prh-terminology` skill for terminology). + - Preserve technical identifiers, paths, `contentPath`, Angular directives. + +4. **Verify the file remains parseable**: + - For JSON: `node -e 'JSON.parse(require("fs").readFileSync("","utf8"))'` must succeed. + +5. **Run lint on this file**: + ```bash + pnpm lint --fix + pnpm lint + ``` + +After processing all files: + +6. **Run migration verifier on the directory**: + ```bash + pnpm exec tsx .agents/skills/update-origin/scripts/verify-migration.ts + ``` + +## Completion Conditions + +- All translatable token changes applied with Japanese translation. +- Non-translatable changes mirrored. +- File remains syntactically valid. +- `pnpm lint` passes. +- `verify-migration.ts` exits 0. + +## Output + +```json +{ + "class": "b", + "kind": "src", + "directory": "", + "files": ["", ...], + "status": "ok" | "needs_review", + "issues": [{ "file": "", "issue": "" }] +} +``` + +## Rules + +- Follow the `prh-terminology` skill for any uncertain terminology. +- Preserve existing Japanese translations for unchanged strings. +- Do **not** commit. The orchestrator commits per directory. diff --git a/.agents/skills/update-origin/roles/migrate-src-class-c.md b/.agents/skills/update-origin/roles/migrate-src-class-c.md new file mode 100644 index 0000000000..d81e38fd4b --- /dev/null +++ b/.agents/skills/update-origin/roles/migrate-src-class-c.md @@ -0,0 +1,80 @@ +# Role: migrate-src-class-c + +Apply class-c source-code migration: > 5 translatable token changes or structural changes in `.ts` / `.html` / `.json`. Used in Phase 4 of the update-origin workflow. + +## Scope + +Class-c means significant translatable-content change: + +- More than 5 lines with translatable strings changed, OR +- Structural reorganization (e.g., navigation tree restructure in `sub-navigation-data.ts`, multi-section template changes) + +The corresponding translated file requires structural follow-through plus translation of all new / changed user-facing strings. + +## Input + +A list of `.en.{ts,html,json}` file paths in a single directory. + +## Procedure + +For each `.en.*` file: + +1. **Read the full diff**: + ```bash + git diff HEAD -- + ``` + +2. **Read both `.en.*` and translated file in full** to understand structural context. + +3. **Apply structural changes to the translated file**: + - Mirror new / removed / reordered entries. + - Preserve existing Japanese translations for entries that survived the restructure (match by path / identifier / key, NOT by line position). + - Translate new entries to Japanese (follow the `prh-terminology` skill for terminology). + - Preserve technical identifiers: `path`, `contentPath`, route names, component selectors, JSON technical keys. + +4. **Verify syntactic validity**: + - JSON: `JSON.parse` must succeed. + - TS / HTML: lint must pass. + +5. **Run lint on this file**: + ```bash + pnpm lint --fix + pnpm lint + ``` + +After processing all files: + +6. **Run migration verifier on the directory**: + ```bash + pnpm exec tsx .agents/skills/update-origin/scripts/verify-migration.ts + ``` + +## Completion Conditions + +- All structural changes mirrored. +- All new / changed user-facing strings translated. +- All previously-existing Japanese translations preserved (match by identifier, not position). +- `pnpm lint` passes. +- `verify-migration.ts` exits 0. + +## Output + +```json +{ + "class": "c", + "kind": "src", + "directory": "", + "files": ["", ...], + "status": "ok" | "needs_review", + "issues": [{ "file": "", "issue": "" }] +} +``` + +If the change scope expanded beyond class-c (e.g., the file is fundamentally a different file now), set status to `needs_review` and report. The orchestrator routes it to the `escalate-src-class-d` role. + +## Rules + +- Always match preserved entries by identifier (`path`, key, selector), not by line number. +- Follow the `prh-terminology` skill for terminology decisions. +- Preserve `path` / `contentPath` / Angular directives / JSON technical keys verbatim. +- Do **not** commit. The orchestrator commits per directory. diff --git a/.agents/skills/update-origin/scripts/classify-md-diff.ts b/.agents/skills/update-origin/scripts/classify-md-diff.ts new file mode 100644 index 0000000000..661d4313ef --- /dev/null +++ b/.agents/skills/update-origin/scripts/classify-md-diff.ts @@ -0,0 +1,125 @@ +#!/usr/bin/env -S pnpm exec tsx +/** + * classify-md-diff.ts + * + * Classify each modified .en.md file into one of four classes by diff scale: + * a — English-only change (no prose impact / code-block / URL / whitespace only) + * b — Small diff (≤ 5 prose lines changed, no paragraph restructure) + * c — Paragraph-level retranslation (≤ 30 prose lines changed) + * d — Page-level restructure (out of scope for /update-origin) + * + * Usage: + * pnpm exec tsx .agents/skills/update-origin/scripts/classify-md-diff.ts [base-ref] + * + * Output (JSON to stdout): + * [{ file, class, directory, metrics: { added, removed, codeOnly, totalLines } }, ...] + */ + +import { execSync } from 'node:child_process'; +import * as path from 'node:path'; + +type Class = 'a' | 'b' | 'c' | 'd'; + +interface Metrics { + added: number; + removed: number; + codeOnly: boolean; + totalLines: number; +} + +interface Classification { + file: string; + class: Class; + directory: string; + metrics: Metrics; +} + +const baseRef = process.argv[2] ?? 'HEAD'; + +function getChangedEnMdFiles(): string[] { + const out = execSync(`git diff --name-only ${baseRef} -- '*.en.md'`, { + encoding: 'utf8', + }); + return out.trim().split('\n').filter(Boolean); +} + +function getDiff(file: string): string { + return execSync(`git diff ${baseRef} -- "${file}"`, { encoding: 'utf8' }); +} + +function fileLineCount(file: string): number { + try { + const out = execSync(`wc -l < "${file}"`, { encoding: 'utf8' }); + return parseInt(out.trim(), 10); + } catch { + return 0; + } +} + +function analyzeDiff(diff: string): Omit { + const lines = diff.split('\n'); + let added = 0; + let removed = 0; + let inCodeBlock = false; + let codeOnly = true; + let inHunk = false; + + for (const line of lines) { + if (line.startsWith('@@')) { + inHunk = true; + // Reset code block tracking at hunk boundaries (heuristic). + inCodeBlock = false; + continue; + } + if (!inHunk) continue; + if (line.startsWith('---') || line.startsWith('+++')) continue; + + const sigil = line[0]; + const content = line.slice(1); + const isFence = /^```/.test(content.trimStart()); + + if (sigil === '+' || sigil === '-') { + if (sigil === '+') added++; + else removed++; + + if (!inCodeBlock && !isFence && content.trim() !== '') { + codeOnly = false; + } + } + + // Toggle code-block state for context lines and changed lines alike. + if (isFence) { + inCodeBlock = !inCodeBlock; + } + } + + return { added, removed, codeOnly }; +} + +function classify(metrics: Metrics): Class { + const total = metrics.added + metrics.removed; + if (total === 0) return 'a'; + if (metrics.codeOnly) return 'a'; + if (total <= 5) return 'b'; + if (total <= 30) return 'c'; + return 'd'; +} + +function main(): void { + const files = getChangedEnMdFiles(); + const results: Classification[] = files.map((file) => { + const diff = getDiff(file); + const partial = analyzeDiff(diff); + const totalLines = fileLineCount(file); + const metrics: Metrics = { ...partial, totalLines }; + return { + file, + class: classify(metrics), + directory: path.dirname(file), + metrics, + }; + }); + process.stdout.write(JSON.stringify(results, null, 2) + '\n'); +} + +main(); diff --git a/.agents/skills/update-origin/scripts/classify-src-diff.ts b/.agents/skills/update-origin/scripts/classify-src-diff.ts new file mode 100644 index 0000000000..26fff7f52a --- /dev/null +++ b/.agents/skills/update-origin/scripts/classify-src-diff.ts @@ -0,0 +1,131 @@ +#!/usr/bin/env -S pnpm exec tsx +/** + * classify-src-diff.ts + * + * Classify each modified .en.{ts,html,json} file into one of four classes + * based on the volume of translatable-token change: + * a — No translatable tokens affected (logic/structure only) + * b — ≤ 5 translatable token lines changed + * c — > 5 translatable token lines or significant structural change + * d — Full rewrite (escalation; do NOT auto-defer like markdown class d) + * + * Usage: + * pnpm exec tsx .agents/skills/update-origin/scripts/classify-src-diff.ts [base-ref] + * + * Output (JSON to stdout): + * [{ file, class, directory, kind, metrics }, ...] + */ + +import { execSync } from 'node:child_process'; +import * as path from 'node:path'; + +type Class = 'a' | 'b' | 'c' | 'd'; +type Kind = 'ts' | 'html' | 'json'; + +interface Metrics { + added: number; + removed: number; + translatableTokens: number; +} + +interface Classification { + file: string; + class: Class; + directory: string; + kind: Kind; + metrics: Metrics; +} + +const baseRef = process.argv[2] ?? 'HEAD'; + +function getChangedFiles(): string[] { + const out = execSync( + `git diff --name-only ${baseRef} -- '*.en.ts' '*.en.html' '*.en.json'`, + { encoding: 'utf8' }, + ); + return out.trim().split('\n').filter(Boolean); +} + +function getDiff(file: string): string { + return execSync(`git diff ${baseRef} -- "${file}"`, { encoding: 'utf8' }); +} + +function detectKind(file: string): Kind { + if (file.endsWith('.en.ts')) return 'ts'; + if (file.endsWith('.en.html')) return 'html'; + if (file.endsWith('.en.json')) return 'json'; + throw new Error(`Unknown file kind: ${file}`); +} + +function isTranslatableLine(content: string, kind: Kind): boolean { + if (kind === 'ts') { + // Heuristics: known user-facing keys with string literals. + return /\b(label|title|description|message|summary|placeholder|tooltip|action)\s*:\s*['"`]/.test( + content, + ); + } + if (kind === 'html') { + // Strip tags; if remaining text has prose-length content, treat as translatable. + const stripped = content.replace(/<[^>]+>/g, '').trim(); + return stripped.length > 3 && /[A-Za-z぀-ヿ一-鿿]/.test(stripped); + } + if (kind === 'json') { + // Heuristic: long quoted string values likely user-facing. + return /:\s*"[^"]{4,}"/.test(content); + } + return false; +} + +function analyzeDiff(diff: string, kind: Kind): Metrics { + const lines = diff.split('\n'); + let added = 0; + let removed = 0; + let translatableTokens = 0; + let inHunk = false; + + for (const line of lines) { + if (line.startsWith('@@')) { + inHunk = true; + continue; + } + if (!inHunk) continue; + if (line.startsWith('---') || line.startsWith('+++')) continue; + + const sigil = line[0]; + if (sigil !== '+' && sigil !== '-') continue; + const content = line.slice(1); + + if (sigil === '+') added++; + else removed++; + + if (isTranslatableLine(content, kind)) translatableTokens++; + } + return { added, removed, translatableTokens }; +} + +function classify(metrics: Metrics): Class { + const total = metrics.added + metrics.removed; + if (metrics.translatableTokens === 0) return 'a'; + if (metrics.translatableTokens <= 5) return 'b'; + if (total < 100) return 'c'; + return 'd'; +} + +function main(): void { + const files = getChangedFiles(); + const results: Classification[] = files.map((file) => { + const kind = detectKind(file); + const diff = getDiff(file); + const metrics = analyzeDiff(diff, kind); + return { + file, + class: classify(metrics), + directory: path.dirname(file), + kind, + metrics, + }; + }); + process.stdout.write(JSON.stringify(results, null, 2) + '\n'); +} + +main(); diff --git a/.agents/skills/update-origin/scripts/verify-codeblock-sync.ts b/.agents/skills/update-origin/scripts/verify-codeblock-sync.ts new file mode 100644 index 0000000000..dbb3a8bfa5 --- /dev/null +++ b/.agents/skills/update-origin/scripts/verify-codeblock-sync.ts @@ -0,0 +1,120 @@ +#!/usr/bin/env -S pnpm exec tsx +/** + * verify-codeblock-sync.ts + * + * Catches a recurring class-a migration bug: an upstream code change inside a + * fenced code block (```...```) was applied to the *.en.md but not mirrored to + * the corresponding *.md, even though wc -l parity holds. + * + * For every *.en.md that has changed since the merge-base with `main` (or a + * given base ref), we walk the diff and inspect each `+` line that lands + * inside a fenced code block on the new side. The same line index in *.md + * MUST contain the identical text — code blocks are shared verbatim. + * + * Exits with code 1 on any mismatch so that the orchestrator treats it as a + * hard gate. + * + * Usage: + * pnpm exec tsx .agents/skills/update-origin/scripts/verify-codeblock-sync.ts [] + * # default base-ref: main + */ +import { execSync } from 'node:child_process'; +import * as fs from 'node:fs'; + +interface Issue { + file: string; + line: number; + en: string; + ja: string; +} + +function changedEnMd(base: string): string[] { + const out = execSync(`git diff --name-only ${base} -- "adev-ja/**/*.en.md"`, { + encoding: 'utf-8', + }); + return out.split('\n').filter(Boolean); +} + +function diffOf(base: string, file: string): string { + return execSync(`git diff ${base} -- ${file}`, { encoding: 'utf-8' }); +} + +function fenceRanges(lines: string[]): Array<[number, number]> { + const ranges: Array<[number, number]> = []; + let inCode = false; + let start = -1; + for (let i = 0; i < lines.length; i++) { + if (/^```/.test(lines[i])) { + if (!inCode) { + inCode = true; + start = i; + } else { + inCode = false; + ranges.push([start, i]); + } + } + } + return ranges; +} + +function inAnyRange(i: number, ranges: Array<[number, number]>): boolean { + return ranges.some(([s, e]) => i > s && i < e); +} + +function main(): void { + const base = process.argv[2] ?? 'main'; + const enFiles = changedEnMd(base); + const issues: Issue[] = []; + + for (const enFile of enFiles) { + const jaFile = enFile.replace(/\.en\.md$/, '.md'); + if (!fs.existsSync(enFile) || !fs.existsSync(jaFile)) continue; + const en = fs.readFileSync(enFile, 'utf-8').split('\n'); + const ja = fs.readFileSync(jaFile, 'utf-8').split('\n'); + if (en.length !== ja.length) continue; // line_mismatch is reported elsewhere + + const ranges = fenceRanges(en); + const diff = diffOf(base, enFile).split('\n'); + let newLineNum = 0; + for (const l of diff) { + const m = l.match(/^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/); + if (m) { + newLineNum = parseInt(m[1], 10) - 1; + continue; + } + if (l.startsWith('+++') || l.startsWith('---')) continue; + if (l.startsWith('+')) { + newLineNum++; + const i = newLineNum - 1; + if (!inAnyRange(i, ranges)) continue; + const enLine = en[i] ?? ''; + const jaLine = ja[i] ?? ''; + if (enLine !== jaLine) { + issues.push({ + file: jaFile, + line: newLineNum, + en: enLine.slice(0, 160), + ja: jaLine.slice(0, 160), + }); + } + } else if (l.startsWith(' ')) { + newLineNum++; + } + } + } + + if (issues.length === 0) { + console.log(`OK: code-block sync verified for ${enFiles.length} changed file(s) since ${base}`); + process.exit(0); + } + + for (const x of issues) { + console.error(`CODEBLOCK_DRIFT: ${x.file}:${x.line}`); + console.error(` EN: ${x.en}`); + console.error(` JA: ${x.ja}`); + } + console.error(`FAILED: ${issues.length} code-block sync issue(s)`); + process.exit(1); +} + +main(); diff --git a/.agents/skills/update-origin/scripts/verify-migration.ts b/.agents/skills/update-origin/scripts/verify-migration.ts new file mode 100644 index 0000000000..ee930b1389 --- /dev/null +++ b/.agents/skills/update-origin/scripts/verify-migration.ts @@ -0,0 +1,172 @@ +#!/usr/bin/env -S pnpm exec tsx +/** + * verify-migration.ts + * + * Deterministic post-migration verification. Exits with code 1 on any failure + * so that the calling skill / agent treats verification as a hard gate. + * + * Checks per target (file or directory): + * - For every *.en.md, the corresponding *.md exists and has identical line count. + * - For every *.en.{ts,html,json}, the corresponding translated file exists. + * (Line count is NOT enforced for source code.) + * + * Usage: + * pnpm exec tsx .agents/skills/update-origin/scripts/verify-migration.ts [ ...] + */ + +import * as fs from 'node:fs'; +import * as path from 'node:path'; +import { execSync } from 'node:child_process'; + +interface Failure { + kind: 'missing_translation' | 'line_mismatch' | 'structure_drift'; + enFile: string; + jaFile: string; + detail?: string; +} + +function collectEnFiles(target: string): string[] { + const stat = fs.statSync(target); + if (stat.isFile()) return /\.en\.(md|ts|html|json)$/.test(target) ? [target] : []; + + const out: string[] = []; + const entries = fs.readdirSync(target, { withFileTypes: true, recursive: true }); + for (const entry of entries) { + if (!entry.isFile()) continue; + if (!/\.en\.(md|ts|html|json)$/.test(entry.name)) continue; + // Node 22: parentPath is set; fall back to name resolution otherwise. + const dir = (entry as fs.Dirent & { parentPath?: string }).parentPath ?? target; + out.push(path.join(dir, entry.name)); + } + return out; +} + +function lineCount(file: string): number { + return fs.readFileSync(file, 'utf8').split('\n').length; +} + +function jaPath(enFile: string): string { + return enFile.replace(/\.en\.(md|ts|html|json)$/, '.$1'); +} + +function changedSince(base: string): Set { + try { + const out = execSync(`git diff --name-only ${base}`, { encoding: 'utf-8' }); + return new Set(out.split('\n').filter(Boolean)); + } catch { + return new Set(); + } +} + +function verifyOne(enFile: string, structuralScope: Set | null): Failure[] { + const failures: Failure[] = []; + const ja = jaPath(enFile); + + if (!fs.existsSync(ja)) { + failures.push({ kind: 'missing_translation', enFile, jaFile: ja }); + return failures; + } + + if (enFile.endsWith('.en.md')) { + // Strip leading BOM so structural-marker regex anchors at column 0. + const stripBom = (s: string) => (s.charCodeAt(0) === 0xfeff ? s.slice(1) : s); + const enLines = stripBom(fs.readFileSync(enFile, 'utf8')).split('\n'); + const jaLines = stripBom(fs.readFileSync(ja, 'utf8')).split('\n'); + if (enLines.length !== jaLines.length) { + failures.push({ + kind: 'line_mismatch', + enFile, + jaFile: ja, + detail: `en=${enLines.length} ja=${jaLines.length}`, + }); + return failures; + } + // Structural-marker alignment is only enforced for files within the + // migration scope (changed vs the configured base ref). Pre-existing drift + // in untouched files is out of scope for /update-origin. + if (structuralScope !== null && !structuralScope.has(enFile) && !structuralScope.has(ja)) { + return failures; + } + // Structural-marker alignment: fenced code fences (```), heading lines, and + // line-prefix docs annotations must sit at the SAME line index on both sides. + // wc -l parity is preserved by the agents but content-level drift (a missing + // blank line compensated by an extra one elsewhere) hides translation bugs. + const ANNOTATION = /^(>\s*)*(IMPORTANT|NOTE|HELPFUL|TIP|CRITICAL|WARNING|TLDR):/; + const FENCE = /^```/; + const HEADING = /^#{1,6}\s/; + const drifts: string[] = []; + for (let i = 0; i < enLines.length; i++) { + const e = enLines[i]; + const j = jaLines[i]; + if (FENCE.test(e) !== FENCE.test(j)) { + drifts.push(`L${i + 1}: code-fence drift`); + } else if (HEADING.test(e) !== HEADING.test(j)) { + drifts.push(`L${i + 1}: heading drift`); + } else if (ANNOTATION.test(e) !== ANNOTATION.test(j)) { + drifts.push(`L${i + 1}: annotation-prefix drift`); + } + if (drifts.length >= 5) break; + } + if (drifts.length > 0) { + failures.push({ + kind: 'structure_drift', + enFile, + jaFile: ja, + detail: drifts.join('; '), + }); + } + } + return failures; +} + +function main(): void { + const args = process.argv.slice(2); + // Optional --base scopes structural-drift checks to files changed since + // that ref. Defaults to "main"; pass --base "" to check ALL files. + let base = 'main'; + const targets: string[] = []; + for (let i = 0; i < args.length; i++) { + if (args[i] === '--base') { + base = args[++i] ?? 'main'; + } else { + targets.push(args[i]); + } + } + if (targets.length === 0) { + console.error('Usage: verify-migration.ts [--base ] [ ...]'); + process.exit(2); + } + + const structuralScope = base === '' ? null : changedSince(base); + + const allEn: string[] = []; + for (const t of targets) { + if (!fs.existsSync(t)) { + console.error(`NOT_FOUND: ${t}`); + process.exit(2); + } + allEn.push(...collectEnFiles(t)); + } + + const failures: Failure[] = []; + for (const en of allEn) failures.push(...verifyOne(en, structuralScope)); + + if (failures.length === 0) { + console.log(`OK: verified ${allEn.length} file(s) under ${targets.join(', ')}`); + process.exit(0); + } + + for (const f of failures) { + if (f.kind === 'missing_translation') { + console.error(`MISSING_TRANSLATION: ${f.jaFile} (for ${f.enFile})`); + } else if (f.kind === 'line_mismatch') { + console.error(`LINE_MISMATCH: ${f.enFile} vs ${f.jaFile} (${f.detail})`); + } else { + console.error(`STRUCTURE_DRIFT: ${f.enFile} vs ${f.jaFile} (${f.detail})`); + } + } + console.error(`FAILED: ${failures.length} issue(s) in ${allEn.length} file(s)`); + process.exit(1); +} + +main(); From a13f7537c1de1d81c1e2f61a28ed737aec0927b5 Mon Sep 17 00:00:00 2001 From: Suguru Inatomi Date: Sun, 4 Oct 2026 12:25:02 +0900 Subject: [PATCH 2/4] feat: add AGENTS.md with project rules for AI agents --- AGENTS.md | 575 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 575 insertions(+) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000000..58be48b50a --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,575 @@ +# AGENTS.md + +Guidance for AI coding agents working in this repository. Skills are in Agent Skills format under `.agents/skills//SKILL.md`. + +## Project Overview + +This is the Japanese translation project for Angular's official documentation site (angular.dev). The repository translates Angular documentation from English to Japanese and hosts the localized site at https://angular.jp. + +For contributor-facing background, see [CONTRIBUTING.md](./CONTRIBUTING.md) (setup, translation flow, guidelines), [UPDATE_ORIGIN.md](./UPDATE_ORIGIN.md) (manual origin update), and [docs/TRANSLATION_WITH_AI.md](./docs/TRANSLATION_WITH_AI.md) (AI translation tool). This file adds the rules agents must follow. + +## Repository Architecture + +### Submodule Structure + +- `origin/`: Git submodule containing the upstream `angular/angular` repository +- `adev-ja/`: Japanese localized version containing both original English files (`.en.md`, `.en.ts`) and their Japanese translations (`.md`, `.ts`) +- `tools/`: Build and translation automation tools + +### Translation File Patterns + +- **English source files**: `filename.en.md`, `filename.en.ts`, `filename.en.json` (snapshots of original content at translation time) +- **Japanese translated files**: `filename.md`, `filename.ts`, `filename.json` (localized versions) +- **Content location**: Primarily in `adev-ja/src/content/` for Markdown documentation +- **Tutorial structure**: Interactive tutorials contain both content files (`README.md`) and configuration files (`config.json`) defining page titles and editor settings + +### Build System + +The project uses a custom TypeScript-based build system with Bazel integration: + +- Build initialization copies and patches files from `origin/adev/` to `build/` directory +- Applies localization patches from `tools/adev-patches/` +- Japanese content from `adev-ja/` overlays the build structure + +## Skills + +Workflow automation lives in `.agents/skills/`. Use the skills below before reaching for ad-hoc shell commands or new tooling. + +| Skill | Purpose | When to use | +| --- | --- | --- | +| `.agents/skills/update-origin/SKILL.md` | Single entry point for syncing upstream `angular/angular` into `adev-ja`. Covers branch, submodule update, diff classification, migration by role, verification, per-class commits, and PR. | Any origin sync. Never perform per-file migration in the orchestrating session itself; follow the role files in `roles/`. | +| `.agents/skills/translate-file/SKILL.md` | First-time translation of a new `.en.md` / `.en.ts` / `.en.json` file into Japanese with strict line-count and anchor-ID rules. | A file is added upstream and you are ready to translate it. NOT for diff sync of an already-translated file (that is the `update-origin` flow). | +| `.agents/skills/prh-terminology/SKILL.md` | Manage the `prh.yml` terminology dictionary. | **Always before adopting a translation for a new technical term** (see "Terminology management" below). | + +### Roles used by update-origin (`.agents/skills/update-origin/roles/`) + +These are instruction files for one batch of work each. If your harness supports delegating to sub-agents, pass the file as the instructions; otherwise follow it yourself in sequence. They are only used by the `update-origin` skill. + +| Role | Purpose | +| --- | --- | +| `migrate-md-class-a.md` | Mirror low-risk markdown changes (code blocks, URLs, prose backtick identifiers). | +| `migrate-md-class-b.md` | Apply small line-localized markdown diffs preserving line correspondence. | +| `migrate-md-class-c.md` | Paragraph-level retranslation. | +| `defer-md-class-d.md` | Class-d defer in either `d-additive` or `d-structural` mode. | +| `migrate-md-roadmap.md` | Special handler for `reference/roadmap.md` (Completed-projects history accumulation). | +| `migrate-src-class-{a,b,c}.md` | Source-code (`.ts` / `.html` / `.json`) migration in three risk tiers. | +| `escalate-src-class-d.md` | Stops the workflow and reports class-d source files for resolution by a person. | + +### Skill scripts (`.agents/skills/update-origin/scripts/`) + +Run with `pnpm exec tsx