Skip to content

fix(dashboard): match correction keywords as whole words, add team keywords - #567

Merged
jeff-r2026 merged 3 commits into
Tencent:mainfrom
SaulMoro:fix/correction-keywords-564
Sep 15, 2026
Merged

jeff-r2026 merged 3 commits into
Tencent:mainfrom
SaulMoro:fix/correction-keywords-564

Conversation

@SaulMoro

@SaulMoro SaulMoro commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

Summary

isCorrectionPrompt tested every prompt with lower.includes(keyword). The built-in undo and redo are common Spanish and Portuguese word endings, so "dame el segundo fichero" sent within 60 s of a Stop was recorded as a course correction. One correction scores CONTRIBUTE_CORRECTION_WEIGHT (20), which equals CONTRIBUTE_SMART_THRESHOLD, so a single false match fired the /teamai-share-learnings nudge and skewed the Human Intervention panel of teamai dashboard.

Two changes:

  1. Whole-word matching for spaced scripts. A keyword that contains Han, Hiragana, Katakana or Hangul keeps substring matching, because those scripts do not separate words. Every other keyword must match as a whole word. The boundary is Unicode-aware ((?<![\p{L}\p{N}_])…(?![\p{L}\p{N}_])), so "malísimo" does not match mal and the identifier test_undo does not match undo; JavaScript \b would have treated í as a boundary.
  2. Team keywords. sharing.intervention.correctionKeywords in teamai.yaml is merged with the built-in list under the same matching rules. The prompt_submit hook resolves the team config and stores a correction: boolean on the event. This has to happen at capture time: ~/.teamai/dashboard/events.jsonl is machine-level and mixes sessions from every team, so rebuildSessions cannot know which team's keywords apply. A side effect is that the full prompt is checked, not only the 200-character summary. Events written before this change carry no flag and fall back to the built-in list over promptSummary, now with whole-word matching.

The built-in list still covers only Chinese, English and Japanese. A correction typed in another language counts once the team adds its words. Interrupts, tool rejections and tool errors are unchanged.

Dashboard, same seeded events, main vs this branch

Latest prompt main this PR
wrong, redo it ⚠ 1 ⚠ 1
esto está mal, rehazlo (event flagged by the hook via a team keyword) none ⚠ 1
dame el segundo fichero (legacy event, no flag) ⚠ 1 none
compare

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)

Test Plan

  • npx tsc --noEmit passed.

  • npx vitest run passed: 226 files, 3154 tests.

  • New unit tests (15): dashboard-collector.test.ts covers the seven false positives from the issue, whole-word and multi-word English keywords, underscore as a word character, Chinese and Japanese substring matching, team keywords with accented letters, full-prompt checking past the 200-char summary, and the legacy fallback in rebuildSessions. types.test.ts covers the schema and getInterventionSharing. hook-handlers.test.ts covers passing team keywords to parseHookEvent, the unreadable-config fallback, and that non-prompt hooks do not read team config.

  • npm run build passed.

  • Real built CLI, user scope: 72 of 72 checks passed across Claude, Codex, CodeBuddy, OpenCode and git, gitlab, github team configs. Each combination used an isolated HOME with a teamai.yaml declaring correctionKeywords: [rehazlo, "no era eso", mal], dispatched stop then prompt-submit through teamai hook-dispatch, and read the persisted event:

    Prompt correction
    dame el segundo fichero false
    esto está mal, rehazlo true
    wrong, redo it true
    no está malísimo, sigue false
    这不对,重来 true
    run test_undo again false
  • Real built CLI, project scope: 5 of 5 checks passed. A git repo initialised with teamai init --self --agent cursor under an isolated HOME, keywords added to .teamai/teamai.yaml. Hooks dispatched with the process cwd set to ~/.cursor and the project passed only through workspace_roots (Cursor payload shape) applied the team keywords; a Claude payload with cwd set to the project did too; a prompt from an unrelated cwd did not see the project's keywords.

  • Real teamai dashboard on main and on this branch with the same 21 seeded events (table above).

  • git diff --check passed.

Related Issues

Fixes #564

Notes for Reviewers

  • teamCorrectionKeywords resolves the team via autoDetectInit(), the same path contributeHintAllowed uses for the Stop hook. hook-dispatch-cli has already chdir'd to the hook payload's cwd by then, so this also works for Cursor, which runs hooks from ~/.cursor; the project-scope check above exercises that path.
  • I did not add built-in Spanish, Portuguese, French, Italian or German keywords. Short words such as "mal" are ambiguous even with word boundaries ("no está mal" means "it's fine"), and the team list covers that need without growing a per-language table in types.ts.
  • Docs: English and Chinese usage guides describe the matching rule, the built-in language coverage, and the new teamai.yaml key in the Human Intervention section and in the teamai.yaml reference. CHANGELOG.md has one Bug Fix entry for the whole-word change and one Feature entry for team keywords and the new event field.
  • The real-CLI matrix exercises hook dispatch and event persistence with isolated homes, a local team directory, and an offline single-repo init. It does not exercise live provider authentication or the agent GUIs.

…ywords

`isCorrectionPrompt` used raw substring matching, so the built-in `undo` and
`redo` fired on ordinary Spanish and Portuguese words ("segundo", "mundo",
"redondo"). One false correction scores 20, which is the share-learnings nudge
threshold on its own.

Keywords in a space-separated script now match as whole words (Unicode-aware,
so accented letters count as letters). Keywords containing Han, Hiragana,
Katakana or Hangul keep substring matching.

Teams can add their own words via `sharing.intervention.correctionKeywords` in
teamai.yaml. The prompt_submit hook resolves them and stores a `correction`
flag on the event, because the machine-level events file mixes sessions from
every team. Events without the flag fall back to the built-in list.

For Tencent#564
… word char

Cursor runs hooks from ~/.cursor and sends the project in workspace_roots, so
resolving the team via autoDetectInit() (process.cwd()) silently dropped the
team's correctionKeywords there. Resolve the project from resolveHookCwd(stdin)
and fall back to the user-scope config.

Underscore joins the word boundary so identifiers such as "test_undo" do not
count as `undo`. Drop the regex cache and fold both keyword lists into one
`.some`. CHANGELOG separates the whole-word fix from the team-keywords feature
and notes the new `correction` event field; both usage guides say the built-in
list still covers only zh/en/ja.
@jeff-r2026
jeff-r2026 self-requested a review September 15, 2026 09:35
hook-dispatch-cli already chdir's to the hook payload's cwd before running
handlers, so autoDetectInit() resolves the right project on Cursor too. The
explicit detectProjectConfig(resolveHookCwd(stdin)) path added in the previous
commit was redundant; drop it and its tests.
@jeff-r2026
jeff-r2026 merged commit d13780c into Tencent:main Sep 15, 2026
1 check passed
@SaulMoro
SaulMoro deleted the fix/correction-keywords-564 branch September 15, 2026 14:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug] Correction detection matches substrings: "segundo" counts as "undo", and Latin languages have no keywords

2 participants