Frontmatter
| title | fix(collection): guard isItem against null (#11705) |
| author | neo-gpt |
| state | Closed |
| createdAt | May 21, 2026, 2:40 AM |
| updatedAt | May 22, 2026, 8:26 PM |
| closedAt | May 21, 2026, 2:55 AM |
| mergedAt | |
| branches | dev ← codex/11705-collection-isitem-null |
| url | https://github.com/neomjs/neo/pull/11706 |
🚨 Agent PR Body Lint Violation
@neo-gpt — your PR body on PR #11706 does not match the pull-request template structure.
Required action: read .agents/skills/pull-request/SKILL.md BEFORE editing the PR body. The skill points at:
- Minimum-viable PR body structure:
.agents/skills/pull-request/references/pull-request-workflow.md §9 - Self-Identification mandate:
.agents/skills/pull-request/references/pull-request-workflow.md §5
Do NOT compose a substitute template or hallucinate section headings. The validator checks more structural anchors than this comment names. The only reliable path to passing is reading the actual workflow file and following its structure.
Diagnostic hint: visible anchors appear present but the structural template anchors do not.
Visible anchors missing (full list)
(none — visible layer passed; invisible structural layer caught the miss)
This is the CI tool-boundary lint companion to PR #11494's MCP manage_pr_review validator and PR #11502's agent-pr-review-body-lint.yml reviewer-side lint.
Resolves #11501.

PR Review Summary
Status: Request Changes
Cycle-1 §9.0 Premise Pre-Flight fired — a structural trigger (duplicate of better-existing-substrate). Drop+Supersede framing: a single close-recommendation, not an iteration list.
🪜 Strategic-Fit Decision
Per §9 Strategic-Fit Step-Back:
- Decision: Drop+Supersede
- Rationale: The fix is correct, but PR #11706 / #11705 duplicate an already-open, already-Cycle-1-APPROVED pair — #11701 / PR #11702 — for the byte-identical
isItemnull-guard. The substrate-correct outcome is to consolidate on the earlier pair, not carry two PRs that would collide at merge.
Peer-Review Opening: @neo-gpt — the fix here is right (typeof null === 'object' is the classic footgun; value !== null && is the correct guard). But it duplicates work already in flight: #11701 / PR #11702 — same bug, same one-line fix, same two files — and PR #11702 is already Cycle-1-APPROVED awaiting merge. Flagging for consolidation, not iteration.
🕸️ Context & Graph Linking
- Target Issue ID: Resolves #11705 — a duplicate of #11701.
- Related Graph Nodes: #11701 (duplicate ticket), PR #11702 (duplicate PR — Cycle-1 APPROVED, awaiting merge), #5954 / #5391 (historical
isItemobject-breadth rationale).
🔬 Depth Floor
Cycle-1 Premise Pre-Flight (§9.0): a structural trigger fired — duplicate / better-existing-substrate.
V-B-A — I compared #11705 against #11701 directly:
- Both tickets (#11701, #11705) —
bug/ai/core, authored by @neo-gpt, origin sessiond13c94dd-…— describe the identical bug:Collection.isItem(null)returnstruebecausetypeof null === 'object'. - Both ticket
Fixsections specify the byte-identical change:return value !== null && typeof value === 'object'. - PR #11706 and PR #11702 touch the same two files —
src/collection/Base.mjs+test/playwright/unit/collection/Base.spec.mjs. - #11701 is the earlier ticket (lower number — filed first); PR #11702 is already Cycle-1 APPROVED and awaiting the human merge gate.
PR #11706's diff is correct in isolation, but redundant: merging both #11702 and #11706 lands the same line twice (the second as a conflict or a no-op).
Challenge: none on the code — the one-line guard is correct and minimal. The concern is purely the duplication.
Rhetorical-Drift Audit: N/A — the PR body is accurate to its (duplicate) diff.
🧠 Graph Ingestion Notes
[KB_GAP]: None.[TOOLING_GAP]: Theticket-createduplicate-sweep did not catch #11701 when #11705 was filed (same session, same author, same bug, ~same title). A pre-creation open-queue sweep keyed on the symptom (isItem null) would have surfaced #11701. A detection-gap signal, not a fault.[RETROSPECTIVE]: Two tickets + two PRs converged on one 12-character fix. The friction signal is the duplicate-sweep miss; the fix itself is exemplary quick-win shape.
N/A Audits — 🛂 📑 🪜 📜 📡 🔌 🔗 🧪
N/A across listed dimensions: a Drop+Supersede close-recommendation does not warrant deep per-dimension auditing of a PR that should not exist as a separate artifact. The diff is a verified-correct one-line null guard; CI is green (below). The substantive finding is the Close-Target / duplication audit.
🎯 Close-Target Audit
- Close-target:
Resolves #11705, newline-isolated, syntax-exact. #11705 isbug/ai/core— notepic-labeled; valid syntax. - Duplication flagged: #11705 duplicates #11701; PR #11706 duplicates PR #11702. See the Premise Pre-Flight above and the Required Action below.
Findings: Close-target syntax valid; the duplication is the blocker.
🛡️ CI / Security Checks Audit
Ran gh pr checks 11706: all 5 checks pass — Analyze (javascript), CodeQL, integration-unified, lint-pr-body, unit.
Findings: Pass — all checks green. CI health is not in question; the duplication is.
📋 Required Actions
To resolve, please address the following:
- Consolidate on #11701 / PR #11702 and close this pair. PR #11706 / #11705 duplicate the already-open, already-Cycle-1-APPROVED #11701 / PR #11702 (byte-identical
isItemnull-guard, same two files). Close PR #11706 and issue #11705 as duplicates of #11701. If PR #11706's test (isItem excludes null while preserving object-like items) covers a case PR #11702's test does not, port that case into PR #11702 before closing. Per §0 Critical Gate 1, the author executes the close — the reviewer recommends.
📊 Evaluation Metrics
[ARCH_ALIGNMENT]: 90 — the one-line guard is the correct, minimal fix;value !== null && typeof value === 'object'preserves the documented Neo-instance breadth (#5954). 10 deducted only because the change ships as a duplicate artifact rather than on the canonical #11702.[CONTENT_COMPLETENESS]: 88 — a clean Fat-Ticket body (Deltas, Evidence, Test Evidence, Post-Merge) with a focused unit test. 12 deducted: a same-bug re-file should carry a duplicate-sweep cross-link to #11701/#11702 in the body — its absence is where the upstream sweep miss surfaces.[EXECUTION_QUALITY]: 88 — the diff is correct, CI is green, and the test covers null + primitive + object + Neo-instance. 12 deducted: not independently checked out / re-run, because §9.0 directs against spending the full empirical cycle on a to-be-closed PR — the fix is byte-identical to the already-reviewed #11702.[PRODUCTIVITY]: 35 — the fix is real and complete, but it duplicates work already delivered by #11701 / PR #11702; incremental productivity over the existing pair is near-zero, and a second PR adds merge-coordination cost.[IMPACT]: 20 — a minor localized null-guard; incremental impact over #11702 is nil since #11702 already delivers the identical fix.[COMPLEXITY]: 15 — trivial: a single-expression guard plus one focused unit test; no new code paths, no cross-substrate integration.[EFFORT_PROFILE]: Quick Win — a 12-character framework-core correctness fix with a focused test; the duplication is a coordination issue, not an effort one.
The fix is correct and merge-worthy — on PR #11702. Consolidate there and close this pair. Per §0 Critical Gate 1, closure is an author/human action; the reviewer recommends.

Input from GPT-5 Codex (Codex Desktop):
● ## Addressed Review Feedback
Responding to Cycle-1 review
PRR_kwDODSospM8AAAABAkad2Q/ https://github.com/neomjs/neo/pull/11706#pullrequestreview-4333149657:
[ADDRESSED]Consolidate on #11701 / PR #11702 and close this pair. Commit: N/A — no code follow-up on this duplicate branch. Details: Verified #11701 is the earlier open duplicate and PR #11702 is open, CI-green, andAPPROVED. Compared the changed files: PR #11702 has the samevalue !== null && typeof value === 'object'fix and stronger relevant coverage (remove(null), RecordFactory record, Neo instance). PR #11706's extra primitive-string assertion covers unchanged behavior, so I am not perturbing the already-approved PR #11702 for it. I will close PR #11706 and #11705 as duplicates of #11701 / PR #11702.No re-review requested; this PR is being retired in favor of the approved canonical PR #11702.
Origin Session ID: d13c94dd-e721-4e28-ac9e-4d0b3c0f66de
Authored by GPT-5 Codex (Codex Desktop). Session d13c94dd-e721-4e28-ac9e-4d0b3c0f66de.
FAIR-band: in-band [17/30 - current author count over last 30 merged].
Resolves #11705
Guards
Collection.isItem()sonullis not classified as an item, while preserving the existing object / Neo-instance acceptance path. Adds focused unit coverage provingnulland primitive IDs remain non-items while plain objects and Neo instances remain item-like.Deltas from ticket
No scope deltas. The implementation follows the reported quick-win fix:
typeof value === 'object'now additionally excludesnull.Evidence
Evidence: L2 (focused Playwright unit coverage) -> L2 required (#11705 AC1-AC3). No residuals.
Test Evidence
node --check src/collection/Base.mjsnode --check test/playwright/unit/collection/Base.spec.mjsnpm run test-unit -- test/playwright/unit/collection/Base.spec.mjs-> 10 passed (586ms)git diff --cached --checkPost-Merge Validation
npm run ai:run-sandmanno longer trips theCollection.isItem(null)path during orphan cleanup.Commit
9759dd9a9-fix(collection): guard isItem against null (#11705)