Repository navigation
OpenConceptLab/ocl_issues#2887 | AI log entries go on the row the AI analysed - #98
Merged
snyaggarwal merged 2 commits intoOct 13, 2026
Merged
Conversation
…the AI analysed fetchRecommendation logged three AI outcomes without a row: a quota or preview-limit stop, from the response or a thrown error, and a failed analysis (the AI Assistant answering with detail). log() then put each on the row open in the side panel, or under null when none was open. So during AutoMatch, or a retry from the grid, the entry showed up on another row, and the row the AI was called for showed nothing. The three calls now pass __index, as the other three AI entries do. A call whose run was superseded still logs on its own row. isSuperseded() guards the run's bookkeeping (the stage and the failure count), not the row's history, which already records such a call's result and its failed attempts. The new test reads fetchRecommendation's source, since the tests can't mount MapProject, and checks that every AIRecommendation* log() call passes __index. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AEzMedE6q8ivk6uh6KeHQz
paynejd
commented
Oct 12, 2026
paynejd
left a comment
Member
Author
There was a problem hiding this comment.
Codex review, pass 1 (codex-cli 0.162.1, commit 9a1a554)
1 finding: 1 P2.
- F1.1 · P2 · src/components/map-projects/tests/aiRecommendationLogs.test.js:43 · The source assertion catches the three omitted arguments but does not meet acceptance criterion 3's bulk-run behavior test. (With bulk rows 1 and 2 and panel row 0 or null, neither recommendation execution nor the resulting row histories are tested. An in-memory check confirmed this assertion still passes when the assignment '__index = _row.__index' is replaced with '__index = rowIndex', recreating incorrect attribution despite every call passing the named variable.)
findings.json
{
"reviewer": "codex-cli 0.162.1",
"perspective": "defects",
"mode": "full",
"commit": "9a1a5541f21aa61fa6677c1f3f0a75294565fdcb",
"verdict": "findings",
"prior": [],
"findings": [
{
"id": "F1.1",
"severity": "P2",
"tag": null,
"file": "src/components/map-projects/__tests__/aiRecommendationLogs.test.js",
"line": 43,
"claim": "The source assertion catches the three omitted arguments but does not meet acceptance criterion 3's bulk-run behavior test.",
"scenario": "With bulk rows 1 and 2 and panel row 0 or null, neither recommendation execution nor the resulting row histories are tested. An in-memory check confirmed this assertion still passes when the assignment '__index = _row.__index' is replaced with '__index = rowIndex', recreating incorrect attribution despite every call passing the named variable.",
"action": "Add behavioral coverage executing the production recommendation and logging logic with mocked AI responses for the three affected branches. Assert entries land under the analysed rows with another row open and with no row open, and cover the chosen superseded-call behavior.",
"lines_delta": null,
"confidence": "high"
}
]
}
Member
Author
Response to Codex pass 1
No P0 or P1 is open, so the Codex loop stops after this pass. |
paynejd
commented
Oct 12, 2026
paynejd
left a comment
Member
Author
There was a problem hiding this comment.
Simplification review (commit 9a1a554)
4 cuts: 3 shrink, 1 yagni.
- S1 · shrink · src/components/map-projects/tests/aiRecommendationLogs.test.js:30 · the AIRecommendation* action filter and the guards around it (all six log() calls in fetchRecommendation are AI calls) → check every log() call in fetchRecommendation (-4)
- S2 · yagni · src/components/map-projects/tests/aiRecommendationLogs.test.js:20 · the one-caller getAILogCalls wrapper and the module-level source → read, parse and walk in the test body (-7)
- S3 · shrink · src/components/map-projects/tests/aiRecommendationLogs.test.js:2 · header lines restating log()'s fallback and the bug's symptom → two short paragraphs (-3)
- S4 · shrink · src/components/map-projects/MapProject.jsx:1718 · the comment's restatement of log()'s default → a two-line rule for callers (-1)
findings.json
{
"reviewer": "claude subagent",
"perspective": "simplify",
"mode": "full",
"commit": "9a1a5541f21aa61fa6677c1f3f0a75294565fdcb",
"verdict": "findings",
"prior": [],
"findings": [
{
"id": "S1",
"severity": null,
"tag": "shrink",
"file": "src/components/map-projects/__tests__/aiRecommendationLogs.test.js",
"line": 30,
"claim": "The action filter and the guards around it (entry?.type !== 'ObjectExpression', property.key?.name, ?.value?.value, String(action), row?.name || null) narrow the walk to AIRecommendation* calls. But all six log() calls in fetchRecommendation are AIRecommendation* calls, so the filter keeps every one, and the guards cover argument shapes no call there has. Checked in a scratch copy: the walk without the filter finds the same six calls, flags the same three on origin/main (lines 5845, 5852, 5885) and flags none at HEAD.",
"scenario": null,
"action": "Check every log() call in fetchRecommendation: `CallExpression({node}) { if(node.callee.name === 'log') calls.push({line: node.loc.start.line, row: node.arguments[1]?.name}) }`. Keep the block body: Babel throws on a visitor that returns a value, so `=> … && calls.push(…)` fails. Change the assert.ok message to 'no log() calls found in fetchRecommendation'.",
"lines_delta": -4,
"confidence": "high"
},
{
"id": "S2",
"severity": null,
"tag": "yagni",
"file": "src/components/map-projects/__tests__/aiRecommendationLogs.test.js",
"line": 20,
"claim": "getAILogCalls has one caller, the file's only test, and the module-level `source` is read only by it. The wrapper adds a comment, a declaration, a return, a closing brace and a call line.",
"scenario": null,
"action": "Do the read, parse and walk in the test body: `const source = readFileSync(…)`, `const calls = []`, `babel.traverse(babel.parseSync(source, {…}), {VariableDeclarator(path) { if(path.node.id.name === 'fetchRecommendation') path.traverse({…}) }})`, then the two asserts. With S1 and S3 this gives a 32-line file. A scratch copy of it passes against HEAD's MapProject.jsx and fails against origin/main's on the three calls this PR fixed.",
"lines_delta": -7,
"confidence": "medium"
},
{
"id": "S3",
"severity": null,
"tag": "shrink",
"file": "src/components/map-projects/__tests__/aiRecommendationLogs.test.js",
"line": 2,
"claim": "Lines 3-5 of the header explain what log() does with an entry passed without a row, which the comment this PR adds above log() in MapProject.jsx now says. They also describe the bug's symptom, which the ticket already records. 'In a bulk run that's the run's row.' (line 10) restates the sentence before it.",
"scenario": null,
"action": "Keep two short paragraphs. First: 'Every entry fetchRecommendation logs goes on the row it analysed, not the row open in the side panel (OpenConceptLab/ocl_issues#2887).' Second: the paragraph on why the test reads source, ending '…each log() call in it must pass __index, the row it was called for.'",
"lines_delta": -3,
"confidence": "medium"
},
{
"id": "S4",
"severity": null,
"tag": "shrink",
"file": "src/components/map-projects/MapProject.jsx",
"line": 1718,
"claim": "The new comment above log() opens by restating its signature and default: 'index is the row the entry is about. Without it the entry goes on the row open in the side panel' says what `index === undefined ? rowIndex : index` says. Only two things in it are new: the rule for callers, and that rowIndex is the row open in the side panel.",
"scenario": null,
"action": "Two lines: `// Work on a row other than the one open in the side panel (a run's, a grid` and `// button's) must pass its index (OpenConceptLab/ocl_issues#2887).`",
"lines_delta": -1,
"confidence": "low"
}
]
}…ter test and log() comment - S1: the test checks every log() call in fetchRecommendation, not only the AIRecommendation* ones. All six are AI entries, and anything it logs is about the row it analyses. - S2: the one-caller helper is inlined into the test. - S3: the test's header no longer restates log()'s fallback or the bug. - S4: the comment above log() keeps only the rule for callers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AEzMedE6q8ivk6uh6KeHQz
Member
Author
Response to the simplification reviewThe slimmer test still fails against origin/main's |
paynejd
commented
Oct 12, 2026
paynejd
left a comment
Member
Author
There was a problem hiding this comment.
Codex check of the cuts (commit 885c64b)
Clean. The check covered 9a1a554..885c64b, the simplification cuts S1 to S4; nothing to revert.
findings.json
{
"reviewer": "codex-cli 0.162.1",
"perspective": "defects",
"mode": "cuts",
"commit": "885c64b",
"verdict": "clean",
"prior": [],
"findings": []
}
snyaggarwal
approved these changes
Oct 13, 2026
snyaggarwal
deleted the
OpenConceptLab/ocl_issues#2887-ai-log-row-index
branch
October 13, 2026 02:26
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed and why
During AutoMatch,
fetchRecommendationlogs each AI Assistant call in the row's history. Three of its sixlog()calls passed no row:AIRecommendationLimitReachedwhen the response is a quota or preview-limit error;AIRecommendationwithextras.errorwhen the AI Assistant answers withdetail;AIRecommendationLimitReachedwhen the call throws a quota or preview-limit error.log()then put the entry on the row open in the side panel, or under anullkey when no row was open. So the failure or quota stop showed up on another row, and the row the AI was called for showed nothing. The same happened outside a run, when the AI retry button in the grid was used on one row while another row was open. The three calls now pass__index, as the other three AI entries already did.Superseded calls still log on their row. The ticket asked whether a call whose run was superseded should log at all. It should.
isSuperseded()guards the run's bookkeeping (the row's stage and the failure count, which now belong to the newer run). It doesn't guard the row's history: a superseded call's result entry and its failed-attempt entries were already logged on its row, and so was its analysis. Both limit-reached sites now match:handlePreviewLimitErrorstill getsisSuperseded() ? null : __indexfor the stage, and the log entry always goes on__index. The comment aboveisSupersedednow says so. A new comment atlog()says that work on a row other than the open one must pass its index.How it was tested
src/components/map-projects/__tests__/aiRecommendationLogs.test.js. It parsesMapProject.jsxwith@babel/core(already a devDependency), finds everylog()call infetchRecommendation(all six areAIRecommendation*entries), and checks that each one passes__index.MapProject.jsxit fails, listing exactly the three calls above (lines 5845, 5852 and 5885, each with no row).fetchRecommendationis a closure over about forty pieces ofMapProjectstate. The test runner has no JSX transform, and the integration tests mirror patterns rather than mountMapProject, so no test here can run a bulk AutoMatch. Extracting the closure just to drive it would be a far bigger change than this fix.runBulkAIAnalysiscallsfetchRecommendation(row, template, true), and__indexis that row's index, so the check covers each row of a bulk run, and single-row calls too.pr.ymlgate, run at the PR's head on Node 22.23.2:start-prod.sh: pass;npm ci: pass;src: exit 0. Locally I ran it with--no-eslintrc -c .eslintrc.json, because an outer checkout's.eslintrc.jsonotherwise cascades in; that is the same config CI uses;npm run test:all: 448 unit tests and 6 integration tests, all pass.Acceptance criteria
__indexas the second argument in the three calls, as the other three do. See the threelog()calls infetchRecommendation.isSuperseded() ? null : __indextohandlePreviewLimitErrorand__indextolog(). The test enforces the second part: a conditional row would fail it.AIRecommendation*entry is recorded under the row being analysed.aiRecommendationLogs.test.jschecks this infetchRecommendation's source rather than by running a bulk run, for the reason given above. Codex flagged that gap as a P2 (F1.1); see below.Review
fetchRecommendationextracted, orMapProjectmounted under test.log()call infetchRecommendation, with no helper and a shorter header, and thelog()comment is shorter.Nothing is open: no P0 or P1, and F1.1 is the one accepted P2.
Docs
No doc describes row logs: there is no
docs/, and the README doesn't cover them. So the code comments are the update: one atlog()and one aboveisSupersededinfetchRecommendation.Not done, on purpose
fetchRecommendationextracted fromMapProject, orMapProjectmounted under test. Either is a refactor beyond this ticket.log()keeps its fallback to the open row. User actions on the open row (mapping, decisions, comments) rely on it.Noticed, not fixed
Nothing beyond the ticket.
closes OpenConceptLab/ocl_issues#2887
🤖 Generated with Claude Code
https://claude.ai/code/session_01AEzMedE6q8ivk6uh6KeHQz
Written by an Octoad session worker (worker-v0.11, recipe lean-loop, flow public>public, run openconceptlab-ocl-issues-2887-20261012-141032).