| name | update-pm-ownership |
| user-invocable | true |
| description | Regenerate Article-PM-Ownership-Reference.mdx after the squad CSV or article list changes |
| argument-hint | describe what changed (e.g. 'new CSV', 'added 5 connector articles', 'PM reassignment for Magic ETL') |
Regenerate Article-PM-Ownership-Reference.mdx to reflect changes in the squad ownership CSV, new articles added to s/article/, or PM reassignments.
The user has provided: $ARGUMENTS
Background
Article-PM-Ownership-Reference.mdx maps every article in s/article/ to its owning Feature (using the same nomenclature as the internal squad CSV) and PM. It is generated by scripts/build-pm-ownership.py, which cross-references:
Feature - Owning Squad, PM, Eng, UX.csv — the authoritative squad/PM roster
docs.json — nav group hierarchy used to infer which feature each article belongs to
- Article frontmatter — title and excerpt used as keyword fallback for articles not clearly placed by nav group
Step 1: Identify what changed
Ask the user (or infer from $ARGUMENTS) which of these scenarios applies:
- New or updated CSV — the squad ownership CSV has been replaced or edited (PM reassignments, new features added, squads reorganized)
- New articles — articles were added to
s/article/ since the reference was last generated
- Both — CSV and articles changed
- Manual correction — a specific article was mis-assigned and the user wants to fix the mapping rule, not just patch the output
For scenario 4, skip to Manual correction below.
Step 2: Review the current CSV
If the CSV changed (scenarios 1 or 3), read the updated file and compare it to what the script currently parses:
head -20 'Feature - Owning Squad, PM, Eng, UX.csv'
Check for:
- New Feature rows — does the script's
NAV_FEATURE dict or KEYWORD_RULES list need a new entry to route articles to this feature? If a new feature was added, articles that should map to it may currently be falling back to a broader catch-all. Ask the user to describe what the new feature covers so you can add the right routing rule.
- Renamed Feature rows — the Feature name is used verbatim as the "Product Group" column in the reference. If a feature was renamed, the script's mapping dicts (
NAV_FEATURE, KEYWORD_RULES) must also be updated so the new name is used instead of the old one.
- PM changes — if only PM names changed (no Feature renames), no script changes are needed; the script re-reads the CSV on each run.
- Duplicate Feature rows — the script already prefers the row with a non-empty PM when duplicates exist (see
scripts/build-pm-ownership.py § Parse CSV). Verify any new duplicates are handled correctly.
Step 3: Review new articles (if applicable)
If articles were added (scenarios 2 or 3), check whether the new articles would be matched correctly by the existing routing logic:
git diff main...HEAD --name-only -- 's/article/*.mdx' | head -30
For each new article, predict what feature the script would assign:
- Look up the article's nav group in
docs.json:
grep "article-filename" docs.json
- Trace that nav group through
NAV_FEATURE in the script. If the group maps to a correct feature, no change is needed.
- If the group is missing from
NAV_FEATURE (new nav group added with the new articles), add a mapping entry.
- If the article is an orphan (not in
docs.json), check whether the title/excerpt would trigger the right KEYWORD_RULES entry.
Step 4: Update the script (if needed)
Open scripts/build-pm-ownership.py and make only the targeted changes identified in steps 2–3. The two places to edit are:
NAV_FEATURE dict (lines ~55–232)
Maps nav group names to Feature names. Add a new entry if:
- A new nav group was added to
docs.json and isn't already covered
- A Feature was renamed (update the value to match the new CSV name)
Format: 'Nav Group Name': 'Feature Name from CSV',
KEYWORD_RULES list (lines ~261–395)
Regex-based fallback used when nav group matching doesn't resolve a feature. Add a new entry if:
- A new feature was added with no obvious nav group mapping
- The title/excerpt of the new articles contains a reliable keyword
Format: (r'keyword pattern', 'Feature Name from CSV'),
Important: Keyword rules are evaluated in order — place more specific patterns before broader ones to avoid false positives. Word-boundary anchors (\b) prevent partial-word matches (e.g. r'\bforms?\b' instead of r'form').
After editing, confirm the Feature names you added exactly match the CSV:
grep "Feature Name" 'Feature - Owning Squad, PM, Eng, UX.csv'
Step 5: Run the script
python3 scripts/build-pm-ownership.py
Review the stats output:
- "Features assigned that are NOT in CSV" — should be empty. If any appear, either the feature name in the script doesn't exactly match the CSV, or the feature was removed from the CSV and needs a new mapping.
- Top feature assignments — spot-check that the article counts per feature look plausible given what changed.
Step 6: Spot-check the output
Verify a sample of the newly changed assignments in Article-PM-Ownership-Reference.mdx:
grep "^| Feature Name" Article-PM-Ownership-Reference.mdx | head -10
grep "new-article-filename.mdx" Article-PM-Ownership-Reference.mdx
grep "(no PM listed)" Article-PM-Ownership-Reference.mdx
If any articles are mis-assigned, return to step 4 and adjust the routing rules. Do not hand-edit the MDX output — all corrections must go through the script so the next regeneration is also correct.
Step 6b: Verify column alignment
The script automatically pads all table columns so every pipe character aligns at the same position in every row (raw-length padding, so pipes align in a plain-text editor). Confirm the output is consistent:
python3 -c "
with open('Article-PM-Ownership-Reference.mdx') as f:
lines = [l.rstrip('\n') for l in f]
widths = set(len(l) for l in lines if l.startswith('| '))
print('Distinct row lengths:', widths)
print('Expected: exactly one value')
"
If more than one length is reported, the padding logic in scripts/build-pm-ownership.py has a bug — check the pad_table() function in the Write MDX section. All 1,822+ rows should be identical in raw length.
Manual correction
If the user reports a specific mis-assignment (e.g. "article X should be Feature Y, not Feature Z"):
- Read the article's frontmatter and check its nav group:
head -10 s/article/filename.mdx
grep "filename" docs.json
- Trace the current assignment through the script's logic to find why it was mis-assigned (wrong
NAV_FEATURE entry, keyword false positive, etc.).
- Fix the root cause in the script (not in the MDX output directly).
- Re-run the script and verify the correction.
- If the fix has downstream effects (other articles that were similarly mis-assigned), surface them to the user before regenerating.
Step 7: Commit
After the user confirms the output looks correct:
git add Article-PM-Ownership-Reference.mdx scripts/build-pm-ownership.py
Commit message format:
docs: regenerate Article PM Ownership Reference
<one line describing what changed — e.g. "updated for June 2026 CSV with Magic ETL PM reassignment">
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Do not commit Feature - Owning Squad, PM, Eng, UX.csv unless the user explicitly asks — they may have pasted a working copy and the CSV is not yet final.
Notes
- The generation script must stay in sync with the CSV's Feature nomenclature. Feature names in
NAV_FEATURE and KEYWORD_RULES must match the CSV's "Feature" column exactly (case-sensitive) — a mismatch causes the "Features assigned that are NOT in CSV" warning.
- The script reads
docs.json at runtime, so any nav restructuring automatically changes group-based assignments on the next run. Nav changes that rename or add groups may need a new NAV_FEATURE entry.
- The
_Last generated: date in the MDX header is hardcoded in the script's output section. After editing the script for a new date, search for _Last generated: and update the date string before running.