triage-integration

v2026.09.24

Diagnose and fix codebase integration failures, whether they occur locally (husky pre-commit/pre-push hooks, lint-staged) or remotely (GitHub Actions CI workflows). Use when a commit is rejected, a push fails, or a CI check goes red. Retrieves logs automatically and provides specific fixes for lint-codebase (typecheck, eslint, oxfmt, knip, spell-check), test-coverage, sync checks, and conventions.

GitHub
Install command
npx skhub add jimmypaolini/triage-integration
Markdown
SKILL.md

Triage Integration Failures

Diagnose failing integration checks in this codebase—whether they occur locally during git commit / git push or remotely in GitHub Actions CI workflows. Map errors to their root causes, read the relevant configuration, apply targeted fixes, and verify locally.

When to Use

  • A git commit or git push is rejected by a local hook (Husky, lint-staged, commitlint, validate-branch-name).
  • A CI check is red on a pull request or push.
  • The user pastes error logs and asks for a fix.
  • Asked to "fix CI", "debug the failing check", or "triage submission errors".

Step 1: Obtain the Logs

Determine if this is a local failure or a CI failure.

Option A: Local Submission Failure

NX_PERF_LOGGING=false lint-staged --config configuration/lint-staged.config.ts --continue-on-error

Invoked directly rather than through nx run codebase:lint-staged. That target's own command still carries NODE_OPTIONS='--import=tsx', which the hook deliberately drops: the flag is inherited by every command lint-staged runs, and its --import=tsx loader preempts the transpiler Nx uses for workspace plugins. esbuild emits no design:paramtypes, so the conformetry plugin's NestJS constructor injection would silently resolve to undefined and the project graph would fail to build. Node reads the TypeScript config natively instead. The hook also measures code (nx run codebase:codometer:write) and clears staged notepad files before running lint-staged.

lint-staged config: configuration/lint-staged.config.ts

Almost every check reaches the staged files through one nx affected run over the lint-code target:

nx affected --target=lint-code --configuration=check --parallel=8 --outputStyle=static --files=<path> --files=<path> …

One --files= flag per staged path, never one comma-separated value: Node is killed by the operating system on a single argument past 1011 bytes, and lint-staged reports that as Task failed to spawn: undefined with no output.

commit-msg hook

File: configuration/.husky/commit-msg

nx run codebase:commitlint --edit=$1
# resolves to:
NODE_OPTIONS='--import=tsx' commitlint --config configuration/commitlint.config.ts --edit <msg-file>

pre-push hook

File: configuration/.husky/pre-push

nx run codebase:validate-branch-name
# resolves to:
validate-branch-name
# reads config from: validate-branch-name.config.cjs

Config: validate-branch-name.config.cjs

lint-staged pattern → command matrix

configuration/lint-staged.config.ts declares three patterns, in this order. A staged package.json matches all three, so all four commands run.

Staged file patternCommands lint-staged runs
{**/package.json,pnpm-workspace.yaml}validation lockfile, run as the CLI directly rather than through its Nx target
**/package.jsonnx run-many --projects=codebase --targets=check-catalog-manifests,sherif,syncpack
* (every staged path)nx affected --target=lint-code --target=gate --target=conformetry-generators --target=conventional-config --target=devcontainer-configuration --target=pull-request-template --target=skill-exclusions --configuration=check --parallel=8 --files=…, then nx run-many --targets=conformetry-validate

There is deliberately no per-file-type row any more. lint-code is an nx:noop aggregator whose dependsOn list holds every static check, and each leaf target declares the config files it reads in its own inputs — so staging configuration/knip.config.ts re-runs knip and cache-hits the rest, with no hand-written mapping to drift. Anything the old table routed by hand (sync-vscode-extensions, markdown-lint, yaml-lint, spell-check) is now reached through that dependsOn list.

Each derivation synchronization target is named in the same invocation rather than reached through dependsOn, because each also publishes on the default branch, and Nx forwards an explicit configuration down dependsOn — so an edge there would let lint-code --configuration=write publish from a branch.

gate is named the same way, but for a different reason: the callidescope Nx plugin infers it with no configuration at all, so it has nothing for dependsOn to forward in the first place. It stays a named sibling because nx affected scopes it to the projects a commit actually touched, the same way it scopes lint-code itself — a commit that deepens one project's call stacks fails that project's own task, which the workspace-wide callidescope --check depth run this replaced never could name.

There is no aggregate synchronize target: each synchronization command is its own Nx target on the synchronization project, named here directly. Naming them alongside keeps a commit gating call-stack depth and derivation drift without a second nx affected call and the extra project graph build it would cost.

Conformetry is the one exception to affected: a generated instance can drift without matching any changed-file glob, so it validates the whole workspace on every commit.

nx sync:check no longer runs on commit at all — the pre-commit hook says so in place of the call it used to make. The generator plugin it checked is emitted into .conformetry on install rather than committed, so no commit can stage it out of date, and every conformetry command re-checks the emitted plugin against the configuration itself.

Triage Procedure

Step 1: Identify the Failing Hook

Read the error output carefully. Determine which hook failed:

  • pre-commit → lint-staged ran Nx targets on staged files
  • commit-msg → commitlint rejected the commit message format
  • pre-push → validate-branch-name rejected the current branch name

Step 2: Read the Error Output

If the user did not paste error output, read the last recorded output from the pre-commit hook:

cat last-lint-staged-output.log

(This file is written automatically after every commit attempt at the workspace root).

Option B: CI Workflow Failure

If the user gave a specific run URL or log output in $ARGUMENTS, fetch only that run:

gh run view <run-id> --log-failed

If no logs are provided, fetch ALL failing runs for the current PR:

gh pr checks --json name,state,link \
  --jq '.[] | select(.state == "FAILURE") | "\(.name) \(.link)"'

Parse the <run-id> from the link and fetch the logs for each failure.

Step 2: Identify the Failing Target

Read the error output carefully to determine:

  • Which Nx target failed (e.g., oxfmt, eslint, typecheck, spell-check)
  • Which project(s) failed (e.g., lexico, caelundas, codebase)
  • The specific error messages from the underlying tool

Step 3: Locate Relevant Configuration

Use the table below to find the exact config file and command for the failing tool. Read the config file before proposing a fix.

prettier and oxfmt (formatting — no composite format target exists)

Both are independent leaf targets that lint-code depends on directly.

TargetCheck commandWrite commandConfig file
prettierprettier --check --config configuration/prettier.config.ts --ignore-path configuration/.prettierignore {projectRoot} (cwd: workspaceRoot)same with --writeconfiguration/prettier.config.ts, configuration/.prettierignore
oxfmtoxfmt -c configuration/oxfmt.config.ts --ignore-path configuration/.oxfmtignore --check {projectRoot} (cwd: workspaceRoot)same with --writeconfiguration/oxfmt.config.ts

Python projects run ruff-format instead — uv run ruff format --check . (cwd: projectRoot), config: pyproject.toml

eslint and oxlint (linting — no composite lint target exists)

Both are independent leaf targets that lint-code depends on directly.

TargetCheck commandWrite commandConfig file
eslinteslint . {args} (cwd: projectRoot)same with --fixproject eslint.config.ts which extends configuration/eslint.config.ts
oxlintoxlint --config configuration/oxlint.config.ts --ignore-path configuration/.oxlintignore {projectRoot}/src (cwd: workspaceRoot)same with --fixconfiguration/oxlint.config.ts

Python projects run ruff-lint instead — uv run ruff check . (cwd: projectRoot), config: pyproject.toml

typecheck

Project typeCommandConfig
TypeScripttsc --noEmit (cwd: projectRoot)project tsconfig.json extends configuration/tsconfig.json
Python (pyright)uv run pyright src/ (cwd: projectRoot)pyproject.toml
Python (ty)uv run ty check src/ (cwd: projectRoot)pyproject.toml

spell-check

Command: cspell --config configuration/cspell.config.yaml '{projectRoot}/**/*.{ts,tsx,js,...,py,ipynb}' --no-progress --gitignore (cwd: workspaceRoot) Config: configuration/cspell.config.yaml

markdown-lint

CommandConfig
checkmarkdownlint-cli2 --config configuration/.markdownlint-cli2.jsonc '{projectRoot}/**/*.md' (cwd: workspaceRoot)configuration/.markdownlint-cli2.jsonc
writesame with --fix

yaml-lint

Command: uv run --project configuration yamllint -c configuration/yamllint.yaml '{projectRoot}' (cwd: workspaceRoot) Config: configuration/yamllint.yaml

stylelint

CommandConfig
checkstylelint --config ../../configuration/stylelint.config.cjs 'src/**/*.css' (cwd: projectRoot)configuration/stylelint.config.cjs
writesame with --fix

knip and vulture (dead-code detection — no composite clean target exists)

knip runs on TypeScript projects and vulture on Python projects; there is no clean target on any project.

TargetCheck commandConfig
knip (TS)knip --config configuration/knip.config.ts --workspace {projectRoot} (cwd: workspaceRoot)configuration/knip.config.ts
vulture (Python)uv run python -m vulture src/ .vulture_whitelist.py --min-confidence 80 (cwd: projectRoot)project .vulture_whitelist.py, global configuration/vulture_whitelist.py

nbstripout (affirmations only — Jupyter notebooks)

Strips cell outputs from .ipynb files before staging. Runs automatically on *.ipynb staged files. Config: applications/affirmations/project.json

Sync checks

Every synchronization command is its own Nx target on the synchronization project — conformetry-generators, conventional-config, devcontainer-configuration, pull-request-template, and skill-exclusions — run directly rather than through a shared aggregate, the same way codebase:codometer and codebase:callidescope are run. There is no sync-* target, no scripts/sync-*.ts script, and no synchronization:synchronize aggregate target — those were retired when the work moved into tools/synchronization. lint-code's dependents name each derivation target directly.

The nestjs-module-graphs and nx-project-graphs targets were retired too, per issue #296: codependix now derives the same NestJS module graphs and Nx neighborhood graphs through its own anchor blocks, checked by nx run codebase:codependix instead.

Check commandWrite commandWhat it validates
nx run synchronization:conformetry-generators:check:writeAGENTS.md generators table matches configuration/conformetry.config.ts
nx run synchronization:conventional-config:check:writeTypes/scopes consistent across configuration/conventional.config.cjs, .vscode/settings.json, skill docs
nx run synchronization:devcontainer-configuration:check:writeCloud and local devcontainer configs share common fields
nx run synchronization:pull-request-template:check:write.github/PULL_REQUEST_TEMPLATE.md in sync with skills and prompts
nx run synchronization:skill-exclusions:check:writeInstalled-skill exclusion lists match skills-lock.json
nx run codebase:sync-vscode-extensions:check:write.vscode/extensions.json matches devcontainer extension lists
nx run codebase:codependix --configuration=check--configuration=writeEach project's README codependix blocks match its real Nx, NestJS, and import graphs

Lesson: If sync checks fail, it means a source of truth was edited without updating its counterpart. Example: editing configuration/conformetry.config.ts requires regenerating the AGENTS.md generators table. Editing configuration/conventional.config.cjs requires regenerating .vscode/settings.json, the PR template, and the types/scopes tables in AGENTS.md and the branch and commit skills.

There is no command that regenerates a skills table of contents. The synchronization module that once maintained the AGENTS.md skills list was retired along with the list itself: agents are handed the installed skills directly, so reading .agents/skills is what tells you which ones exist. A new skill needs no synchronization run — a skill added to skills-lock.json does, because skill-exclusions derives the exclusion blocks from it.

check-lockfile (package.json / pnpm-workspace.yaml changes)

Command: validation lockfile, the lockfile check

lint-staged runs it as the CLI directly rather than through its codebase:check-lockfile Nx target, so one command does not cost another project graph build. Run it by hand through the target:

pnpm exec nx run codebase:check-lockfile

commitlint (commit-msg hook)

Command: NODE_OPTIONS='--import=tsx' commitlint --config configuration/commitlint.config.ts --edit <msg-file> Config: configuration/commitlint.config.ts


Step 3: Apply Targeted Fixes

⚠️ CRITICAL RULE: Validate Fixes But Never Run lint-staged

After applying fixes with --configuration=write, you MUST:

  • ❌ DO NOT run lint-staged (this would stage the unstaged fixes, defeating the purpose)
  • ❌ DO NOT run git commit
  • ❌ DO NOT run git push
  • ❌ DO NOT invoke submit, checkout-branch, or create-pull-request skills
  • ✅ DO validate that fixes work by running the exact failing Nx target with --configuration=check
  • ✅ DO leave all modified files unstaged so the user can review and stage them

Auto-Fixable Targets (run --configuration=write)

For these targets, run the Nx target with --configuration=write to auto-fix. Do NOT stage the modified files — leave them unstaged so the user can review the changes before staging.

# Format errors (oxfmt, prettier — no composite `format` target exists)
pnpm exec nx affected --target=oxfmt,prettier --configuration=write --files=<staged-files>

# Lint errors with auto-fix (ESLint --fix, oxlint --fix — no composite `lint` target exists)
pnpm exec nx affected --target=eslint,oxlint --configuration=write --files=<staged-files>

# Markdown lint with auto-fix
pnpm exec nx affected --target=markdown-lint --configuration=write --files=<staged-files>

# Unused code (knip --fix, vulture whitelist — no composite `clean` target exists)
pnpm exec nx affected --target=knip,vulture --configuration=write --files=<staged-files>

# Sync checks: run the write configuration to regenerate the out-of-sync file
pnpm exec nx run synchronization:conformetry-generators:write
pnpm exec nx run synchronization:conventional-config:write
pnpm exec nx run synchronization:devcontainer-configuration:write
pnpm exec nx run synchronization:pull-request-template:write
pnpm exec nx run synchronization:skill-exclusions:write
pnpm exec nx run codebase:sync-vscode-extensions:write
pnpm exec nx run codebase:codependix --configuration=write

# Or every derivation at once
pnpm exec nx run-many --targets=conformetry-generators,conventional-config,devcontainer-configuration,pull-request-template,skill-exclusions --configuration=write

Validate Fixes Passed

After applying fixes with --configuration=write, run the exact failing target with --configuration=check to confirm the fixes work. Use the same --files argument as the original failing lint-staged run:

# Validate format fixes worked
pnpm exec nx affected --target=oxfmt,prettier --configuration=check --files=<staged-files>

# Validate lint fixes worked
pnpm exec nx affected --target=eslint,oxlint --configuration=check --files=<staged-files>

# Validate markdown-lint fixes worked
pnpm exec nx affected --target=markdown-lint --configuration=check --files=<staged-files>

# Validate knip/vulture fixes worked
pnpm exec nx affected --target=knip,vulture --configuration=check --files=<staged-files>

# Validate sync checks fixed themselves (re-run the check configuration)
pnpm exec nx run-many --targets=conformetry-generators,conventional-config,devcontainer-configuration,pull-request-template,skill-exclusions --configuration=check
pnpm exec nx run codebase:sync-vscode-extensions:check

If all --configuration=check commands pass, the fixes are confirmed working. Proceed to Step 5.

If a --configuration=check command still fails, review the error output and apply additional manual fixes as needed, then re-validate that target.

✅ Best practice: Re-run the exact original composite nx affected --target=... --configuration=check --files=... command after each fix batch. A first pass may reveal additional lint/type errors hidden behind the first failure, so continue iterating until the full original command is green.

Manual Fix Required

Failing targetWhat to do
typecheckRead the TypeScript/Python errors. TS: Use optional chaining array[0]?.property for index access, avoid any, require explicit function return types, and use import { type Foo } for type-only imports. Python: Note that [tool.ty] config must remain in the project-level pyproject.toml (not workspace root).
spell-checkEither fix the typo, or if it's a valid word (false negative), add it to the most relevant dictionary in configuration/.cspell/ (e.g. lexico.txt, tooling.txt). If a suitable category doesn't exist, create a new dictionary file in configuration/.cspell/, register it in configuration/cspell.config.yaml, and refactor existing dictionaries to move any relevant words into the new dictionary. As a fallback, add it directly to words in configuration/cspell.config.yaml.
markdown-lint (MD024/no-duplicate-heading)Check whether duplicate headings also contain duplicate content. If content is verbatim duplicated, remove only the extra block. If content differs, keep both content blocks and rename one heading to a distinct, specific title.
yaml-lintFix YAML syntax errors per configuration/yamllint.yaml rules.
stylelintFix CSS issues per configuration/stylelint.config.cjs.
check-lockfileRun pnpm install to regenerate pnpm-lock.yaml. Do NOT stage the lockfile — leave it unstaged for the user to review. Lesson: Any manual change to a package.json or workspace config often requires this.
commitlintFix the commit message. See format below.
validate-branch-nameRename the branch with git branch -m <new-valid-name>. See format above.
vulture (Python)Fix the flagged unused code, or add a # noqa comment. The project-local .vulture_whitelist.py and global configuration/vulture_whitelist.py are both read. Min-confidence is 80.

Invalid Branch Name (pre-push hook)

Required format: <type>/<scope>-<description>

  • type and scope: see Valid Types and Scopes below
  • description: lowercase kebab-case (e.g., user-auth, fix-build-script)

Exempt branches (no validation): main, copilot/*, dependabot/*, renovate/*

To fix, rename the current branch:

git branch -m <new-valid-name>
# example:
git branch -m feat/lexico-user-auth

Read validate-branch-name.config.cjs to see the full regex and error message.

Commitlint Errors (commit-msg hook)

Required format: <type>(<scope>): <gitmoji> <subject>

  • type and scope: see Valid Types and Scopes below
  • gitmoji: Required emoji at the start of the subject (e.g., ✨ feat, 🐛 fix, 📝 docs, ✅ test, ♻️ refactor, ⚡️ perf, 🔧 chore, 👷 ci, ⬆️ deps)
  • subject: lowercase, imperative mood, no period, max 128 chars total
  • No body or footer — all context in the subject

Read configuration/commitlint.config.ts for the full rule set before amending.

Valid Types and Scopes

<!-- types-start -->
TypeDescription
featA new feature or capability that adds value for users
fixA bug fix that addresses a specific issue or problem
docsDocumentation, AGENTS.md, SKILL.md, README, and planning files
testAdding or correcting unit, integration, or end-to-end tests
refactorCode restructuring that neither fixes a bug nor adds a feature
styleFormatting, whitespace, or code structure changes with no semantic effect
perfA code change that improves performance (caching, query optimization, etc.)
choreHousekeeping that doesn't modify src or test files (gitignore, editor config, etc.)
ciGitHub Actions workflows, composite actions, and CI/CD scripts
buildBuild system, Vite/Docker/Helm config, or external dependency integration
revertReverts a previous commit
<!-- types-end --> <!-- scopes-start -->
ScopeDescription
ic-suiteIn-house code measurement and validation toolchains (Callidescope, Codependix, Codometer, Conformetry) and their shared conventions
affirmationsPython Jupyter notebook application for LangGraph affirmation generation
caelundasNode.js CLI for astronomical calendar generation (NASA JPL ephemeris)
configurationWorkspace root config files (tsconfig, eslint, vitest, nx.json, etc.)
conformetryCode generator templates and validation tests for generated instances
dependenciesDependency version changes (upgrades, additions, removals via pnpm)
deploymentsGitHub Actions workflows and CI/CD pipeline configuration
documentationMarkdown docs, skills, planning files, and AGENTS.md files
infrastructureHelm charts, Terraform configs, and Kubernetes resources
JimmyPaoliniStatic GitHub profile README project (markdown and assets)
lexicoTanStack Start SSR Latin dictionary web app with Supabase backend
lexico-componentsShared React/shadcn component library
lexico-entitiesShared TypeORM entities and GraphQL types
lexico-ingestionData ingestion scripts for Lexico
meanderawGreek meander (key/fret) SVG generator CLI and the composable motif/modifier library it reads
sempientorLexical gap discovery CLI that surveys English for morphological, phonotactic, and semantic gaps and coins words to fill them
callidescopeCall stack tracing and linting CLI, the configuration package it reads, and the packages that build and render its call graph
codependixDependency graph export CLI, the configuration package it reads, and the package that judges the graphs against declared rules
codometerCode statistics measurement CLI, the configuration package it reads, and the packages that diff and render its pull request change report
no-releaseEscape hatch: suppress semantic-release for any commit type
releaseVersion bumps and release commits generated by semantic-release
reportingPull request change report generation and the packages that diff and render it
scriptsShell and TypeScript scripts in scripts/ (sync, setup, utilities)
testingVitest configuration, shared test utilities, and coverage setup
synchronizationSynchronization application and commands for automating workflows
validationValidation CLI and the checks it runs, such as pull request metadata
<!-- scopes-end -->

Step 5: Report Errors Found and Fixes Implemented

The skill ends here. Do NOT do anything else.

At the end of the run, report a summary to the user covering:

  1. Errors found — for each failing hook/target, state:

    • Which hook failed (pre-commit, commit-msg, or pre-push)
    • Which Nx target or tool produced the error (e.g., eslint, oxfmt, typecheck)
    • The specific error messages or rule violations
  2. Fixes implemented — for each fix, state:

    • Whether it was an auto-fix command (e.g., format --configuration=write) or a manual code/configuration edit
    • Which files were modified (all left unstaged — the user must review and git add them before retrying the commit)
  3. Validation — for each fix, state:

    • The command(s) run to validate the fix
    • The pass/fail result for each command
  4. Remaining actions — if any issues require user action (e.g., manual typecheck fixes, commit message amend, branch rename), list them explicitly so the user knows what still needs to be done before committing. If all validations passed, state "All fixes validated. Ready to review, stage, and commit."

Use this report template:

Errors Found
- <hook>: <target/tool> — <error message>

Fixes Implemented
- <auto-fix command and/or manual change>
- Files changed: <file list>

Validation
- <check command> — ✅ PASSED

Remaining Actions
- <none | explicit follow-up actions>

Common Patterns

SymptomCauseFix
Unexpected token, Expected whitespaceoxfmt/prettier format check failednx affected --target=oxfmt,prettier --configuration=write
error ... @typescript-eslint/...ESLint rule violationnx affected --target=eslint,oxlint --configuration=write or manual fix
MD024/no-duplicate-headingDuplicate markdown heading in same fileIf duplicated content is identical, remove one block; if content differs, keep both and rename one heading
Type 'X' is not assignable to 'Y'TypeScript type errorManual fix — check tsconfig.json strict settings
Unknown word in cspellUnrecognized wordAdd it to the most relevant dictionary in configuration/.cspell/ (e.g. lexico.txt, tooling.txt). If a suitable category doesn't exist, create a new dictionary file, register it in configuration/cspell.config.yaml, and refactor existing dictionaries to move any relevant words into the new dictionary. As a fallback, add it to configuration/cspell.config.yaml words list.
lockfile needs updatepnpm-lock.yaml out of syncpnpm install (leave lockfile unstaged for user to review)
sync check failedGenerated file is out of dateRun the corresponding :write target
subject may not be emptycommitlint missing subjectAmend commit message to correct format
Knip: Unused exportExport not used anywhereRemove export or add to knip ignoreBinaries/ignoreExports

References

Hooks

Tool Configurations

ToolConfig File
ESLint (base)configuration/eslint.config.ts
oxlintconfiguration/oxlint.config.ts
oxfmtconfiguration/oxfmt.config.ts
Prettierconfiguration/prettier.config.ts, configuration/.prettierignore
TypeScript (base)configuration/tsconfig.json
cspellconfiguration/cspell.config.yaml
markdownlintconfiguration/.markdownlint-cli2.jsonc
yamllintconfiguration/yamllint.yaml
stylelintconfiguration/stylelint.config.cjs
knipconfiguration/knip.config.ts
Ruff + pyrightpyproject.toml
commitlintconfiguration/commitlint.config.ts
validate-branch-namevalidate-branch-name.config.cjs
Conventional commits (types/scopes)configuration/conventional.config.cjs
check-lockfiletools/validation/src/modules/lockfile/lockfile.constants.ts

Git Conventions

Root Cause & Prevention

You are in triage mode because a proactive validation step was skipped.

After resolving these failures, remind the user: use the validate-code skill before committing to catch all of these issues before pre-commit hooks run.

Specifically, after every implementation task:

# Auto-fix format, lint, and unused-code issues
pnpm exec nx affected --target=lint-code --configuration=write --base=main

# Verify all checks pass — do not commit until this is clean
pnpm exec nx affected --target=lint-code --configuration=check --base=main

Running this loop before staging catches 100% of the pre-commit hook failures this skill handles — formatting, linting, typecheck, spell-check, unused code, and sync checks — without any pre-commit interruption.

Discovery
Tags

No tags published for this skill.

Version
Latest version metadata

Version

v2026.09.24

Published

Sep 24, 2026

Category

Uncategorized

License

MIT

Source path

.agents/skills/triage-integration

Default branch

main

Latest commit

5ac136d

Tree SHA

9af071d