Skip to content

fix(deps): patch frontend audit security floors - #1623

Draft
seonghobae wants to merge 15 commits into
developfrom
autoresearch/frontend-sec-bump
Draft

fix(deps): patch frontend audit security floors#1623
seonghobae wants to merge 15 commits into
developfrom
autoresearch/frontend-sec-bump

Conversation

@seonghobae

@seonghobae seonghobae commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Current authority — 2026-09-12

  • protected base: develop@042b0c70531b229af3acbd0421a2f23098d848b3
  • exact head: 9d6d1e09e1ad52e57a1a3162c9984cceea4b2cb8
  • exact tree: d99f83fc482ba948271f45ae2f7251036dd190ad
  • lifecycle: Draft / canonical frontend dependency-security owner / Nano ID repair + repository product-security + current Strix GREEN / central CodeQL compatibility RED / current-head independent approval pending / do not merge
  • owned scope: frontend dependency manifests, generated lockfile, Naruon security-floor contracts, and repository guidance. The canonical Gap ledger remains solely owned by #1602.

Nano ID owner repair

RED 8175f7f04a8624a681a509eb8b446c7a3ff7bd5d proves the owner lock resolved Nano ID 3.3.18 below the current 3.3.19 regression floor. GREEN 9d6d1e09e1ad52e57a1a3162c9984cceea4b2cb8 synchronizes workspace override, package/snapshot lock records, regression contract, and CHANGELOG. Exact tree d99f83fc482ba948271f45ae2f7251036dd190ad passed 21 focused security contracts, full backend 1,826 passed / 33 skipped, frontend 51 files / 437 tests, frozen install, ESLint, TypeScript, and the 16-route production build.

Reality RED → causal repair succession

This branch raises reviewed floors for Next.js, sharp, js-yaml, Vitest, @vitest/coverage-v8, and Nano ID, and binds package/workspace declarations to the root pnpm importer plus referenced package/snapshot records.

  • RED a6715c9c845ed5ad48929fd3261bb07ce4b802e5 → GREEN 21897d8ccd11ee4a2d7a3df5551db12c92c9ec71: Vitest package/snapshot matching must be non-empty before every resolution is checked.
  • RED 15fecaaeabe6faaedf7e4c3f8991eb3add1dd0c5 → GREEN 17a7618eda2b212b691f08fa936e042b34258fc9: bind root importer specifier/version and peer-qualified snapshots to the manifest.
  • 54871419193734918e79c89b54c65761f04ae095: additionally require the importer-resolved base packages record.
  • RED 8175f7f04a8624a681a509eb8b446c7a3ff7bd5d → GREEN 9d6d1e09e1ad52e57a1a3162c9984cceea4b2cb8: pin Nano ID 3.3.19 consistently across workspace override and generated package/snapshot records.

The separate js-yaml contract already fails closed on missing resolutions and binds workspace overrides plus the ESLint consumer, so no duplicate implementation was added.

Single-writer repair

Concurrent commit 4e66036b6257a03369f690eb0dba942bc260b284 wrote #1623 evidence into docs/product-technical-gap-baseline.md, even though #1602 is the established canonical writer. Ordinary descendant d8327d4904f38588b6b6883338aafb575256a19b restored the exact protected-develop ledger blob while preserving dependency/test ancestry. The current PR comparison still excludes the Gap ledger.

Current exact-head hosted evidence — 9d6d1e09...

All predecessor receipts are historical only. Current exact 9d6d1e09e1ad52e57a1a3162c9984cceea4b2cb8 has its own hosted evidence:

  • Application CI 34695440146SUCCESS (backend (Python 3.14) and frontend GREEN)
  • Security Scan 34695440169SUCCESS (dependency-review, trivy-fs, osv-scan, scorecard GREEN)
  • SAST Semgrep 34695440144SUCCESS
  • Bandit 34695440132SUCCESS
  • Docker 34695440319SUCCESS; all three validation images are GREEN
  • opencode-reviewSUCCESS
  • coverage-source-tree / coverage-evidenceSUCCESS
  • Required Noema Review 34695439320 / job 103558064359SUCCESS
  • direct GitHub Advanced Security CodeQL on this source head → SUCCESS / no new changed-code alert
  • Required Strix 34695439368 / job 103558112215SUCCESS. The exact 9d6d1e09... scan admitted the live PR head, materialized the target, provisioned the contextual-orchestrator sidecar, installed Strix, ran Run Strix (quick) to completion, collected and uploaded reports, and completed the required job successfully. Do not transfer predecessor d8327d4... provider failure to this head.
  • CodeQL PR 34695440148FAILURE in the compatibility/publication wrapper: Python, Actions, and JavaScript/TypeScript compatibility jobs fail while the same run's Dispatch current-head CodeQL scan later succeeds. This remains central owner orchestration evidence, not a dependency-source SARIF finding.

There is no qualifying formal independent approval bound to 9d6d1e... yet. Older CodeRabbit reviews and the predecessor d8327d4... approval remain historical and do not authorize this moved head. A fresh CodeRabbit exact-range review has been requested; until a formal current-commit verdict materializes, this remains a merge blocker.

Predecessor d8327d4... evidence — historical only

Predecessor d8327d4904f38588b6b6883338aafb575256a19b had repository/product checks GREEN and CodeRabbit review 5186169435 formally APPROVED. It reproduced a central CodeQL ordering failure and a terminal Required Strix provider-availability failure. Those receipts were valid for that predecessor but are not current-head checks, current-head approval, or current-head Strix verdicts after the Nano ID source change. Current exact Strix is now GREEN.

Merge boundary

Keep Draft. Merge requires the unchanged final exact head to have terminal-success for every then-live required context, including the central CodeQL compatibility path; the security-floor invariants must remain GREEN on the synthetic merge; all valid current-head threads/findings must be resolved; and a qualifying independent approval must be bound after the last source-changing commit. If an external owner gate fails, repair it in its canonical owner and consume only the released/versioned result—no provider/model pin, paid fallback, caller-side retry policy, copied central workflow, synthetic status, dummy/no-op requeue, self-approval, bypass/admin merge, force push, destructive rebase, second Gap-ledger writer, or gate weakening.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 9, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-09T03:59:35.531464Z e8a54fc Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@coderabbitai

coderabbitai Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

📝 Walkthrough

Walkthrough

The frontend updates Next.js, eslint-config-next, Vitest, js-yaml, and sharp versions. New tests validate manifest values, workspace overrides, lockfile consistency, and dependency security floors.

Changes

Frontend security floor enforcement

Layer / File(s) Summary
Update frontend dependency versions
frontend/package.json, frontend/pnpm-workspace.yaml
Next.js and eslint-config-next update to 16.3.4. Vitest packages update to 4.1.11. The workspace pins js-yaml to 4.3.2 and sharp to 0.35.4.
Enforce frontend framework and image floors
backend/tests/test_frontend_framework_security_floor.py
Tests parse dependency and lockfile versions, validate importer and snapshot entries, enforce Next.js and sharp floors, and reject below-floor drift.
Validate js-yaml and Vitest floors
backend/tests/test_frontend_framework_security_floor.py, backend/tests/test_js_yaml_dependency_security.py
Tests validate js-yaml overrides and resolutions, Vitest manifest and lockfile versions, and the js-yaml dependency used by @eslint/eslintrc.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: 🟡 Moderate · up to 21897

The frontend dependency update raises Vitest security floors, but its validation can still accept a mismatched root lockfile resolution or missing referenced snapshot. This can allow future dependency drift to bypass the intended security-floor contract and should be corrected before merge.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: updating frontend dependencies to address security-floor requirements.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch autoresearch/frontend-sec-bump

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@seonghobae
seonghobae marked this pull request as draft September 9, 2026 02:55

Copy link
Copy Markdown
Contributor Author

@codex review

Please review only the current exact head d76f7b450888d42040ca090ed9b9924e08f01881. The predecessor review at a3421f3... predates the security-floor regression test and is not current-head evidence. Focus on the Next.js/sharp security-floor contract, generated lock consistency, and whether the regression can fail closed without introducing false GREENs.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d76f7b4508

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread backend/tests/test_frontend_framework_security_floor.py Outdated
Comment thread backend/tests/test_frontend_framework_security_floor.py Outdated

Copy link
Copy Markdown
Contributor Author

@codex review

Please review exact head e8a54fc5156ac3ffbb79bc8418c5125d7dcdea60 only. Focus on the structural pnpm-lock.yaml security-floor validation added after the two P2 findings on predecessor d76f7b...: root importer/specifier/resolution/snapshot identity, eslint-config-next parity, lock/workspace sharp override identity, and rejection of every below-floor next@ / sharp@ key in both packages and snapshots. Do not transfer predecessor check or review evidence.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Nice work!

Reviewed commit: e8a54fc515

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@seonghobae
seonghobae marked this pull request as ready for review September 9, 2026 03:55
coderabbitai[bot]
coderabbitai Bot previously approved these changes Sep 9, 2026
@seonghobae

Copy link
Copy Markdown
Contributor Author

exact-head validation receipt

Head: e8a54fc5156ac3ffbb79bc8418c5125d7dcdea60; base: develop@042b0c70531b229af3acbd0421a2f23098d848b3.

  • Official patch floors verified: Next.js >=16.3.3 for GHSA-2xp9-vwfh-vxw4 and CVE-2026-75604; sharp >=0.35.4 for GHSA-rgj7-g3m4-5g8c. This head uses Next.js 16.3.4 and sharp 0.35.4.
  • corepack pnpm install --frozen-lockfile: exit 0, 490 packages, pnpm 11.5.3.
  • focused backend security-floor contract: 5 passed, terminal exit 0.
  • next build --webpack: exit 0; 16 routes generated under Next.js 16.3.4.
  • first broad frontend run overlapped the production build and failed 10 tests, mostly at the unchanged 5-second limit. This is not counted as a pass. After the build completed, each failed file was rerun without widening its timeout: Dashboard 5/5, Data 12/12, Projects 4/4, Prompt Studio 7/7, total 28/28, terminal exit 0.

Visual Inspection: directly inspected the production build in a real browser at 1280×720, locale ko, on /, /data, and /projects, with backend-unavailable empty/error states. Main content remained readable without overlap or clipping. A pre-existing top-header horizontal scrollbar still pushes later workspace destinations outside the initial viewport; that customer-facing responsive gap is outside this dependency-only delta and must not be treated as passed responsive coverage. Browser verification ended before stopping the local server; server exit 130 is the expected operator stop, while the logged proxy-target configuration failures reflect the intentionally absent backend.

@seonghobae

Copy link
Copy Markdown
Contributor Author

Exact-head dependency repair — 2026-09-09

Current head: b97f42f16f2dfedccc1e98812c88117132ef8126

The prior clean audit became stale when GitHub published new js-yaml and Vitest advisories. This head repairs the canonical dependency owner:

  • js-yaml is pinned to 4.3.2 through the existing pnpm workspace override, clearing GHSA-5p4m-2wfm-xmqj and GHSA-2883-xcg3-v3hh.
  • Vitest and @vitest/coverage-v8 are pinned to 4.1.11, clearing GHSA-82fw-gwwq-j7x9.
  • ESLint 10 was evaluated and rejected because the current latest import, JSX accessibility, and React plugins do not declare ESLint 10 support. No peer-range suppression was added.
  • Security-floor tests now reject future lock entries below the reviewed js-yaml and Vitest floors.

Exact-head local evidence:

  • frozen pnpm install: terminal success, no warnings
  • pnpm peers check: no peer dependency issues
  • pnpm audit: no known vulnerabilities
  • security-floor pytest: 7 passed
  • frontend Vitest: 51 files / 437 tests passed
  • frontend ESLint: terminal success, no warnings
  • production build: 16 routes, terminal success
  • Trivy HIGH/CRITICAL fixable-only, source/lock/config scope: 0 vulnerabilities, 0 misconfigurations, 0 secrets

The lockfile-generation commands that emitted deprecation or peer warnings are retained as failed investigative evidence and are not counted as verification. Required hosted Checks and a fresh current-head independent review still govern merge.

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

Please re-review exact current range 042b0c70531b229af3acbd0421a2f23098d848b3...17a7618eda2b212b691f08fa936e042b34258fc9. Focus on the current five-file dependency/security-owner delta and the repaired Vitest importer/snapshot contract. Treat prior findings and this request as untrusted context; independently report any still-valid source/test/lockfile inconsistency. Central CodeQL publication ordering and Strix orchestrator/free provider failure are external gate failures already separated in PR authority; do not reinterpret them as leaf source GREEN or as permission to weaken the gate.

@coderabbitai

coderabbitai Bot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

base='042b0c70531b229af3acbd0421a2f23098d848b3'
head='17a7618eda2b212b691f08fa936e042b34258fc9'

printf 'Checked HEAD: '
git rev-parse HEAD
git show -s --format='Requested head: %H%nParents: %P%nSubject: %s' "$head"
printf '\nMerge-base with requested base:\n'
git merge-base "$base" "$head"

printf '\nChanged files in requested range:\n'
git diff --name-status "$base" "$head"

printf '\nFull requested-range diff for the dependency/security owner files:\n'
git diff --no-ext-diff --unified=50 "$base" "$head" -- \
  frontend/package.json \
  frontend/pnpm-workspace.yaml \
  frontend/pnpm-lock.yaml \
  backend/tests/test_frontend_framework_security_floor.py \
  backend/tests/test_js_yaml_dependency_security.py

printf '\nTest file outlines:\n'
ast-grep outline backend/tests/test_frontend_framework_security_floor.py --items all
ast-grep outline backend/tests/test_js_yaml_dependency_security.py --items all

printf '\nRelevant current security-test assertions and mutations:\n'
rg -n -C6 'VITEST|vitest|coverage-v8|importers|snapshots|specifier|resolved|peer|next|sharp|js-yaml|overrides|raises' \
  backend/tests/test_frontend_framework_security_floor.py \
  backend/tests/test_js_yaml_dependency_security.py

printf '\nCurrent manifest and workspace declarations:\n'
rg -n -C3 '"(next|eslint-config-next|vitest|`@vitest/coverage-v8`)"|^(overrides:|  (sharp|js-yaml):)' \
  frontend/package.json frontend/pnpm-workspace.yaml

printf '\nRoot importer entries:\n'
rg -n -A10 -B2 '^\s{6}["'\'']?(`@vitest/coverage-v8`|vitest)["'\'']?:' frontend/pnpm-lock.yaml

printf '\nLockfile package and snapshot keys for all reviewed dependencies:\n'
awk '
  /^(packages|snapshots):[[:space:]]*$/ { section=$1; sub(/:$/, "", section); next }
  section != "" && /^  ['\''"]?(`@vitest`\/coverage-v8|vitest|next|sharp|js-yaml)`@/` {
    print section ":" NR ":" $0
  }
' frontend/pnpm-lock.yaml

printf '\nLockfile override declarations:\n'
awk '
  /^overrides:[[:space:]]*$/ { on=1; print NR ":" $0; next }
  on && /^[^[:space:]]/ { exit }
  on { print NR ":" $0 }
' frontend/pnpm-lock.yaml

printf '\nFocused current-head repair commit diff:\n'
git diff --no-ext-diff --unified=80 "${head}^" "$head" -- \
  backend/tests/test_frontend_framework_security_floor.py \
  backend/tests/test_js_yaml_dependency_security.py

Length of output: 182609


@seonghobae I reviewed only 042b0c70531b229af3acbd0421a2f23098d848b3...17a7618eda2b212b691f08fa936e042b34258fc9.

P1: Bind each Vitest importer resolution to its base packages key

In backend/tests/test_frontend_framework_security_floor.py, Line 179 checks that the root importer version has an exact peer-qualified snapshot. The test only checks that some matching packages key exists in Line 182 through Line 190.

A malformed lock can retain, for example, vitest@4.1.12 in packages and retain the exact vitest@4.1.11(...) snapshot, while removing vitest@4.1.11 from packages. The test passes because both sections remain non-empty and meet the floor. The lockfile no longer contains the package record required by the importer-resolved base version.

Require the base version from each importer entry to exist in packages. Remove the peer suffix before forming the package key. Keep the complete peer-qualified value for the snapshot key.

resolved_version = str(importer_entry["version"])
base_version = resolved_version.split("(", 1)[0]

assert f"{package_name}@{base_version}" in lock["packages"], (
    f"root importer {package_name} resolution must reference an existing package record"
)
assert f"{package_name}@{resolved_version}" in lock["snapshots"], (
    f"root importer {package_name} resolution must reference an existing snapshot"
)

Add a negative test that removes the exact base package key while retaining another compliant version. This test will reject the false-GREEN path.

I found no other source or lockfile inconsistency in the five-file range. The manifest, root importer, peer-qualified snapshots, js-yaml override, Next.js floor, and sharp override are otherwise consistent. I applied the provided pnpm v9 importer and peer-qualified snapshot rule during this review.


🧠 Learnings used
Learnt from: seonghobae
Repo: ContextualWisdomLab/naruon PR: 1623
File: backend/tests/test_frontend_framework_security_floor.py:183-187
Timestamp: 2026-09-09T20:22:07.409Z
Learning: For pnpm v9 lockfiles in the frontend workspace, root package dependency records are stored at `importers["."]["devDependencies"]`, not `packages["."]`. Root importer `version` values can include peer-qualified suffixes, so tests must remove the suffix before comparing the resolved semantic version, while snapshot existence checks must use the exact peer-qualified version key.

You are interacting with an AI system.

Copy link
Copy Markdown
Contributor Author

Fresh dependency-security receipt from the narrow reply-SLA repair #1670; no dependency source was copied or modified.

Security Scan run 34683084178, job 103525212031 (trivy-fs), materialized exact a980e6bc2c1cd571ef267a85a7930a8438faf1b9 (parent protected develop 042b0c70531b229af3acbd0421a2f23098d848b3). The refreshed Trivy database returned three findings in inherited frontend/pnpm-lock.yaml: next CVE-2026-75604 (CRITICAL 9.1), next GHSA-2xp9-vwfh-vxw4 (CRITICAL 9.5), sharp GHSA-rgj7-g3m4-5g8c (HIGH 8.0). SARIF upload succeeded and enforcement failed closed. Job: https://github.com/ContextualWisdomLab/naruon/actions/runs/34683084178/job/103525212031

This is fresh evidence on a develop descendant, not proof that current security-owner head 17a7618eda2b212b691f08fa936e042b34258fc9 also fails: verify its actual resolved dependency versions against the same advisories and current database. Preserve your importer/snapshot/floor contracts. Required acceptance is a real scan of the owner correction and subsequent integrated descendant, with no ignores or exception.

#1670 now advances only its service/tests/investigation to b2ee9c792938ae9dfe2d3e8310cffd628469168f; lockfiles remain unchanged. It stays Draft and does not treat inherited vulnerabilities as safe. This is a handoff of evidence to the canonical dependency-security owner, not a competing source edit or instruction to bypass your existing CodeQL/Strix/approval gates.

Reject a lock that retains a different patched package record while losing the importer-resolved base record.

Signed-off-by: Seongho Bae <me@seonghobae.me>
Keep the focused local result distinct from hosted checks, review, merge, and deployment evidence.

Signed-off-by: Seongho Bae <me@seonghobae.me>
Restore the protected develop ledger blob on this branch. PR #1602 remains the sole writer for docs/product-technical-gap-baseline.md while the complete dependency and regression-test delta stays in ancestry.
seonghobae added a commit that referenced this pull request Sep 12, 2026
Advance the canonical ledger to 1.7 with the exact #1623 owner head/tree, test evidence, recreated checks, and remaining review gate. The dependency branch no longer writes this file.

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

Please re-review the unchanged exact head d8327d4904f38588b6b6883338aafb575256a19b against develop@042b0c70531b229af3acbd0421a2f23098d848b3. The last source-changing commit only restored docs/product-technical-gap-baseline.md to the protected-base blob to repair the #1602 single-writer violation; the effective PR scope is now the seven dependency/security/guidance files listed by GitHub. Please submit a formal current-head approval only if this exact range has no actionable finding.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Sep 12, 2026
@coderabbitai

coderabbitai Bot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

base='042b0c70531b229af3acbd0421a2f23098d848b3'
head='d8327d4904f38588b6b6883338aafb575256a19b'

printf 'Checked worktree HEAD: '; git rev-parse HEAD
git show -s --format='Requested head: %H%nParents: %P%nSubject: %s' "$head"
printf '\nMerge base:\n'
git merge-base "$base" "$head"

printf '\nChanged files in requested range:\n'
git diff --name-status "$base" "$head"

printf '\nDiff stat and exact changed paths:\n'
git diff --stat "$base" "$head"
git diff --name-only "$base" "$head"

printf '\nDocumentation restoration check:\n'
git diff --no-ext-diff --summary "$base" "$head" -- docs/product-technical-gap-baseline.md
if git diff --quiet "$base" "$head" -- docs/product-technical-gap-baseline.md; then
  printf 'docs/product-technical-gap-baseline.md matches base\n'
else
  printf 'docs/product-technical-gap-baseline.md differs from base\n'
fi

printf '\nDependency/security file diffs:\n'
git diff --no-ext-diff --unified=55 "$base" "$head" -- \
  frontend/package.json \
  frontend/pnpm-workspace.yaml \
  frontend/pnpm-lock.yaml \
  backend/tests/test_frontend_framework_security_floor.py \
  backend/tests/test_js_yaml_dependency_security.py

printf '\nSecurity-test outlines:\n'
ast-grep outline backend/tests/test_frontend_framework_security_floor.py --items all
ast-grep outline backend/tests/test_js_yaml_dependency_security.py --items all

printf '\nSecurity-contract implementation and mutation coverage:\n'
rg -n -C5 'vitest|coverage-v8|importers|snapshots|specifier|resolved|peer|next|sharp|js-yaml|override|raises|packages' \
  backend/tests/test_frontend_framework_security_floor.py \
  backend/tests/test_js_yaml_dependency_security.py

printf '\nManifest and workspace declarations:\n'
rg -n -C3 '"(next|eslint-config-next|vitest|`@vitest/coverage-v8`)"|^(overrides:|  (sharp|js-yaml):)' \
  frontend/package.json frontend/pnpm-workspace.yaml

printf '\nRelevant root importer entries:\n'
rg -n -A10 -B2 '^\s{6}["'\'']?(`@vitest/coverage-v8`|vitest|next|eslint-config-next)["'\'']?:' frontend/pnpm-lock.yaml

printf '\nRelevant package and snapshot keys:\n'
awk '
  /^(packages|snapshots):[[:space:]]*$/ { section=$1; sub(/:$/, "", section); next }
  section != "" && /^  ['\''"]?(`@vitest`\/coverage-v8|vitest|next|sharp|js-yaml)`@/` {
    print section ":" NR ":" $0
  }
' frontend/pnpm-lock.yaml

printf '\nLockfile override declarations:\n'
awk '
  /^overrides:[[:space:]]*$/ { active=1; print NR ":" $0; next }
  active && /^[^[:space:]]/ { exit }
  active { print NR ":" $0 }
' frontend/pnpm-lock.yaml

printf '\nRead-only structural contract scan:\n'
python3 - <<'PY'
from pathlib import Path
import json
import re

manifest = json.loads(Path("frontend/package.json").read_text())
lines = Path("frontend/pnpm-lock.yaml").read_text().splitlines()
section = None
in_root = False
in_dev = False
importers = {}
keys = {"packages": set(), "snapshots": set()}
current = None

def clean(value):
    return value.strip().strip("'\"")

for line_no, raw in enumerate(lines, 1):
    if raw in ("importers:", "packages:", "snapshots:"):
        section = raw[:-1]
        in_root = in_dev = False
        current = None
        continue
    if section == "importers":
        if re.match(r"^  \.:\s*$", raw):
            in_root, in_dev = True, False
            continue
        if in_root and re.match(r"^    devDependencies:\s*$", raw):
            in_dev = True
            continue
        if in_dev:
            match = re.match(r"^      (.+?):\s*$", raw)
            if match:
                current = clean(match.group(1))
                importers[current] = {}
                continue
            match = re.match(r"^        (specifier|version):\s*(.*?)\s*$", raw)
            if match and current:
                importers[current][match.group(1)] = clean(match.group(2))
    elif section in keys:
        match = re.match(r"^  (.+?)@(.+?):\s*$", raw)
        if match:
            keys[section].add((clean(match.group(1)), clean(match.group(2))))

for name in ("vitest", "`@vitest/coverage-v8`"):
    entry = importers.get(name)
    print(f"{name}: importer={entry!r}; manifest={manifest['devDependencies'].get(name)!r}")
    assert entry is not None
    assert entry["specifier"] == manifest["devDependencies"][name]
    resolved = entry["version"]
    base_version = resolved.split("(", 1)[0]
    assert base_version == manifest["devDependencies"][name]
    assert (name, base_version) in keys["packages"]
    assert (name, resolved) in keys["snapshots"]
    print(f"  package={name}@{base_version}; snapshot={name}@{resolved}")

floors = {
    "next": (16, 3, 3),
    "sharp": (0, 35, 4),
    "js-yaml": (4, 3, 2),
    "vitest": (4, 1, 11),
    "`@vitest/coverage-v8`": (4, 1, 11),
}
for section, entries in keys.items():
    for name, floor in floors.items():
        matching = []
        for entry_name, version in entries:
            if entry_name != name:
                continue
            match = re.match(r"^(\d+)\.(\d+)\.(\d+)", version)
            if match:
                parsed = tuple(map(int, match.groups()))
                matching.append((version, parsed))
        assert matching, f"missing {name} entries in {section}"
        assert all(parsed >= floor for _, parsed in matching), (
            f"below-floor {name} entry in {section}: {matching}"
        )
        print(f"{section} {name}: {len(matching)} entries; floor satisfied")
PY

Length of output: 189210


🏁 Script executed:

#!/bin/bash
set -u

base='042b0c70531b229af3acbd0421a2f23098d848b3'
head='d8327d4904f38588b6b6883338aafb575256a19b'
lock='frontend/pnpm-lock.yaml'

printf '%s\n' 'Guidance-file changes:'
git diff --no-ext-diff --unified=30 "$base" "$head" -- AGENTS.md CLAUDE.md

printf '\n%s\n' 'Exact Vitest lock-key checks:'
for key in \
  "  '`@vitest/coverage-v8`@4.1.11':" \
  "  '`@vitest/coverage-v8`@4.1.11(vitest@4.1.11)':" \
  "  vitest@4.1.11:" \
  "  vitest@4.1.11(`@types/node`@26.1.2)(`@vitest/coverage-v8`@4.1.11)(jsdom@30.0.1)(vite@8.1.4(`@types/node`@26.1.2)(jiti@2.7.0)):"; do
  if grep -Fqx "$key" "$lock"; then
    printf 'PRESENT: %s\n' "$key"
  else
    printf 'MISSING: %s\n' "$key"
  fi
done

printf '\n%s\n' 'Search for stale reviewed dependency resolutions in package and snapshot sections:'
awk '
  /^(packages|snapshots):[[:space:]]*$/ { section=$1; sub(/:$/, "", section); next }
  section != "" && /^  ['\''"]?(`@vitest`\/coverage-v8|vitest|next|sharp|js-yaml)`@/` {
    print section ":" NR ":" $0
  }
' "$lock"

printf '\n%s\n' 'YAML parser availability and parsed contract check:'
if ruby -e 'require "yaml"' 2>/dev/null; then
  ruby -ryaml -rjson - <<'RUBY'
manifest = JSON.parse(File.read("frontend/package.json"))
workspace = YAML.safe_load(File.read("frontend/pnpm-workspace.yaml"), aliases: false)
lock = YAML.safe_load(File.read("frontend/pnpm-lock.yaml"), aliases: false)
dev = lock.fetch("importers").fetch(".").fetch("devDependencies")

%w[vitest `@vitest/coverage-v8`].each do |name|
  entry = dev.fetch(name)
  declared = manifest.fetch("devDependencies").fetch(name)
  resolved = entry.fetch("version").to_s
  base = resolved.split("(", 2).first
  snapshot = "#{name}@#{resolved}"
  package = "#{name}@#{base}"
  raise "specifier mismatch for #{name}" unless entry.fetch("specifier") == declared
  raise "resolved version mismatch for #{name}" unless base == declared
  raise "missing package #{package}" unless lock.fetch("packages").key?(package)
  raise "missing snapshot #{snapshot}" unless lock.fetch("snapshots").key?(snapshot)
  puts "#{name}: importer, package, and snapshot binding valid"
end

raise "workspace js-yaml override mismatch" unless workspace.dig("overrides", "js-yaml").to_s == "4.3.2"
raise "lock js-yaml override mismatch" unless lock.dig("overrides", "js-yaml").to_s == "4.3.2"
raise "workspace sharp override mismatch" unless workspace.dig("overrides", "sharp").to_s == "0.35.4"
raise "lock sharp override mismatch" unless lock.dig("overrides", "sharp").to_s == "0.35.4"
puts "workspace and lock override bindings valid"
RUBY
else
  printf '%s\n' 'Ruby YAML parser is unavailable.'
fi

printf '\n%s\n' 'Changed-file integrity check:'
git diff --check "$base" "$head"
printf 'git diff --check passed\n'

Length of output: 12867


@seonghobae I reviewed only 042b0c70531b229af3acbd0421a2f23098d848b3...d8327d4904f38588b6b6883338aafb575256a19b.

I found no actionable finding in the seven-file dependency, security, and guidance scope.

The restored docs/product-technical-gap-baseline.md matches the protected-base blob.

The vitest and @vitest/coverage-v8 root importer entries match frontend/package.json. Each entry resolves to 4.1.11. Each entry has its required base-version packages record and exact peer-qualified snapshots record.

The lockfile contains only the reviewed resolutions for the checked dependency keys: Next.js 16.3.4, sharp 0.35.4, js-yaml 4.3.2, Vitest 4.1.11, and @vitest/coverage-v8 4.1.11. The workspace and lockfile overrides also agree for sharp and js-yaml.

The negative tests cover importer drift, missing resolutions, missing importer snapshot records, and missing importer base package records. This closes the previously identified Vitest false-GREEN path.

I also checked the guidance changes. They describe the same pnpm importer contract that the tests enforce. git diff --check passed.

I applied the pnpm v9 importer and peer-qualified snapshot rule from the provided repository learning during this review.


🧠 Learnings used
Learnt from: seonghobae
Repo: ContextualWisdomLab/naruon PR: 1623
File: backend/tests/test_frontend_framework_security_floor.py:183-187
Timestamp: 2026-09-09T20:22:07.409Z
Learning: For pnpm v9 lockfiles in the frontend workspace, root package dependency records are stored at `importers["."]["devDependencies"]`, not `packages["."]`. Root importer `version` values can include peer-qualified suffixes, so tests must remove the suffix before comparing the resolved semantic version, while snapshot existence checks must use the exact peer-qualified version key.
✅ Action performed

Comments resolved and changes approved.

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Canonical owner repair completed non-force on exact predecessor d8327d4904f38588b6b6883338aafb575256a19b.

  • RED 8175f7f04a8624a681a509eb8b446c7a3ff7bd5d: security contract requires Nano ID 3.3.19; predecessor lock resolved 3.3.18.
  • GREEN 9d6d1e09e1ad52e57a1a3162c9984cceea4b2cb8, tree d99f83fc482ba948271f45ae2f7251036dd190ad: workspace override, package/snapshot resolution and CHANGELOG now agree on 3.3.19.
  • Focused security contracts: 21 passed.
  • Full backend: 1,826 passed / 33 skipped; frontend: 51 files / 437 tests passed.
  • Frozen pnpm install, ESLint, TypeScript, and Next.js production build (16 routes) passed; git diff --check passed.

Hosted exact-head Checks and an exact-head independent approval remain merge gates. Keep Draft.

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

Please review only exact current range develop@042b0c70531b229af3acbd0421a2f23098d848b3...9d6d1e09e1ad52e57a1a3162c9984cceea4b2cb8. The last source-changing commit is the Nano ID 3.3.19 owner repair; verify that workspace override, importer/package/snapshot records and the security-floor regression remain fail-closed together with the existing Next.js/sharp/js-yaml/Vitest invariants. Do not transfer predecessor d8327d4... approval. Current CodeQL compatibility failure is a central owner gate, and current Strix is still in progress; neither is leaf-source evidence or permission to weaken a gate.

@coderabbitai

coderabbitai Bot commented Sep 12, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

base='042b0c70531b229af3acbd0421a2f23098d848b3'
head='9d6d1e09e1ad52e57a1a3162c9984cceea4b2cb8'

printf 'worktree_head='; git rev-parse HEAD
git show -s --format='requested_head=%H%nparents=%P%nsubject=%s' "$head"
printf 'merge_base='; git merge-base "$base" "$head"
printf '\nChanged paths in requested range:\n'
git diff --name-status "$base" "$head"
printf '\nRange diff for dependency/security owner files:\n'
git diff --no-ext-diff --unified=70 "$base" "$head" -- \
  frontend/package.json frontend/pnpm-workspace.yaml frontend/pnpm-lock.yaml \
  backend/tests/test_frontend_framework_security_floor.py \
  backend/tests/test_js_yaml_dependency_security.py \
  AGENTS.md CLAUDE.md

printf '\nSecurity-test outlines:\n'
ast-grep outline backend/tests/test_frontend_framework_security_floor.py --items all
ast-grep outline backend/tests/test_js_yaml_dependency_security.py --items all

printf '\nRelevant test assertions and mutation coverage:\n'
rg -n -C5 'nanoid|vitest|coverage-v8|importers|snapshots|specifier|resolved|peer|next|sharp|js-yaml|override|packages|raises' \
  backend/tests/test_frontend_framework_security_floor.py \
  backend/tests/test_js_yaml_dependency_security.py

printf '\nManifest and workspace declarations:\n'
rg -n -C3 '"(nanoid|next|eslint-config-next|vitest|`@vitest/coverage-v8`)"|^(overrides:|  (nanoid|sharp|js-yaml):)' \
  frontend/package.json frontend/pnpm-workspace.yaml

printf '\nRelevant root importer entries:\n'
rg -n -A10 -B2 '^\s{6}["'\'']?(nanoid|`@vitest/coverage-v8`|vitest|next|eslint-config-next)["'\'']?:' frontend/pnpm-lock.yaml

printf '\nRelevant package and snapshot keys:\n'
awk '
  /^(packages|snapshots):[[:space:]]*$/ { section=$1; sub(/:$/, "", section); next }
  section != "" && /^  ['\''"]?(`@vitest`\/coverage-v8|vitest|next|sharp|js-yaml|nanoid)`@/ {
    print section ":" NR ":" $0
  }
' frontend/pnpm-lock.yaml

printf '\nLockfile overrides:\n'
awk '
  /^overrides:[[:space:]]*$/ { active=1; print NR ":" $0; next }
  active && /^[^[:space:]]/ { exit }
  active { print NR ":" $0 }
' frontend/pnpm-lock.yaml

printf '\nRead-only dependency structural contract:\n'
python3 - <<'PY'
from pathlib import Path
import json, re, sys

manifest = json.loads(Path("frontend/package.json").read_text())
workspace_text = Path("frontend/pnpm-workspace.yaml").read_text()
lines = Path("frontend/pnpm-lock.yaml").read_text().splitlines()
section = None
in_root = in_dev = False
current = None
importers = {}
keys = {"packages": set(), "snapshots": set()}
overrides = {}

def clean(v):
    return v.strip().strip("'\"")

for n, raw in enumerate(lines, 1):
    if raw in ("overrides:", "importers:", "packages:", "snapshots:"):
        section = raw[:-1]
        in_root = in_dev = False
        current = None
        continue
    if section == "overrides":
        m = re.match(r"^  (.+?):\s*(.+?)\s*$", raw)
        if m:
            overrides[clean(m.group(1))] = clean(m.group(2))
    elif section == "importers":
        if re.match(r"^  \.:\s*$", raw):
            in_root, in_dev = True, False
        elif in_root and re.match(r"^    devDependencies:\s*$", raw):
            in_dev = True
        elif in_dev:
            m = re.match(r"^      (.+?):\s*$", raw)
            if m:
                current = clean(m.group(1))
                importers[current] = {}
            else:
                m = re.match(r"^        (specifier|version):\s*(.*?)\s*$", raw)
                if m and current:
                    importers[current][m.group(1)] = clean(m.group(2))
    elif section in keys:
        m = re.match(r"^  (.+?)@(.+?):\s*$", raw)
        if m:
            keys[section].add((clean(m.group(1)), clean(m.group(2))))

def semver(v):
    m=re.match(r"^(\d+)\.(\d+)\.(\d+)", v)
    return tuple(map(int,m.groups())) if m else None

def report(condition, message):
    print(("PASS" if condition else "FAIL"), message)
    if not condition:
        global_failed[0] = True

global_failed=[False]
for name, group in (("vitest","devDependencies"), ("`@vitest/coverage-v8`","devDependencies")):
    entry=importers.get(name, {})
    declared=manifest.get(group,{}).get(name)
    resolved=str(entry.get("version",""))
    base=resolved.split("(",1)[0]
    report(entry.get("specifier")==declared, f"{name}: importer specifier equals manifest")
    report(base==declared, f"{name}: importer base version equals manifest")
    report((name,base) in keys["packages"], f"{name}: base package key exists")
    report((name,resolved) in keys["snapshots"], f"{name}: exact snapshot key exists")

for name, floor in {
    "nanoid":(3,3,19), "next":(16,3,3), "sharp":(0,35,4),
    "js-yaml":(4,3,2), "vitest":(4,1,11), "`@vitest/coverage-v8`":(4,1,11)
}.items():
    for sec in ("packages","snapshots"):
        versions=[v for n,v in keys[sec] if n==name and semver(v)]
        report(bool(versions), f"{name}: non-empty {sec} resolutions")
        report(bool(versions) and all(semver(v)>=floor for v in versions),
               f"{name}: all {sec} resolutions meet {floor}")

for name, expected in {"nanoid":"3.3.19","sharp":"0.35.4","js-yaml":"4.3.2"}.items():
    wm = re.search(rf"(?m)^  {re.escape(name)}:\s*(.+?)\s*$", workspace_text)
    workspace_value=clean(wm.group(1)) if wm else None
    report(workspace_value==expected, f"{name}: workspace override equals {expected}")
    report(overrides.get(name)==expected, f"{name}: lockfile override equals {expected}")

if global_failed[0]:
    sys.exit(1)
PY

printf '\nWhitespace errors:\n'
git diff --check "$base" "$head"
printf 'diff_check=passed\n'

Length of output: 226545


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' 'Nano ID security test outline and content:'
ast-grep outline backend/tests/test_frontend_nanoid_security.py --items all
cat -n backend/tests/test_frontend_nanoid_security.py

printf '%s\n' 'Exact Nano ID repair commit:'
git log --format='%H %s' --all -- frontend/pnpm-workspace.yaml | head -20
git diff --no-ext-diff --unified=100 '9d6d1e09e1ad52e57a1a3162c9984cceea4b2cb8^' '9d6d1e09e1ad52e57a1a3162c9984cceea4b2cb8' -- \
  frontend/pnpm-workspace.yaml frontend/pnpm-lock.yaml backend/tests/test_frontend_nanoid_security.py

printf '%s\n' 'Parsed YAML lockfile bindings and security floors:'
ruby -ryaml -rjson - <<'RUBY'
manifest = JSON.parse(File.read("frontend/package.json"))
workspace = YAML.safe_load(File.read("frontend/pnpm-workspace.yaml"), aliases: false)
lock = YAML.safe_load(File.read("frontend/pnpm-lock.yaml"), aliases: false)
root = lock.fetch("importers").fetch(".")
packages = lock.fetch("packages")
snapshots = lock.fetch("snapshots")

def version(key, name)
  raise "wrong key: #{key}" unless key.start_with?("#{name}@")
  key.delete_prefix("#{name}@").split("(", 2).first.split(".").map(&:to_i)
end
def matching(table, name)
  table.keys.select { |key| key.start_with?("#{name}@") }
end

{
  "nanoid" => [3,3,19],
  "next" => [16,3,3],
  "sharp" => [0,35,4],
  "js-yaml" => [4,3,2],
  "vitest" => [4,1,11],
  "`@vitest/coverage-v8`" => [4,1,11],
}.each do |name, floor|
  ["packages", "snapshots"].each do |section|
    entries = matching(lock.fetch(section), name)
    puts "#{section} #{name}: #{entries.inspect}"
    raise "no #{section} entry for #{name}" if entries.empty?
    raise "below floor #{section} #{name}" if entries.any? { |key| version(key, name) < floor }
  end
end

%w[vitest `@vitest/coverage-v8`].each do |name|
  entry = root.fetch("devDependencies").fetch(name)
  declared = manifest.fetch("devDependencies").fetch(name)
  resolved = entry.fetch("version").to_s
  base = resolved.split("(", 2).first
  raise "#{name} specifier" unless entry.fetch("specifier") == declared
  raise "#{name} version" unless base == declared
  raise "#{name} package" unless packages.key?("#{name}@#{base}")
  raise "#{name} snapshot" unless snapshots.key?("#{name}@#{resolved}")
  puts "#{name}: importer, package, and snapshot binding valid"
end

%w[nanoid sharp js-yaml].each do |name|
  puts "#{name}: workspace=#{workspace.fetch("overrides").fetch(name)} lock=#{lock.fetch("overrides").fetch(name)}"
end
RUBY

printf '%s\n' 'Whitespace errors:'
git diff --check '042b0c70531b229af3acbd0421a2f23098d848b3' '9d6d1e09e1ad52e57a1a3162c9984cceea4b2cb8'

Length of output: 27329


🏁 Script executed:

#!/bin/bash
set -euo pipefail

ruby -ryaml -rjson - <<'RUBY'
manifest = JSON.parse(File.read("frontend/package.json"))
workspace = YAML.safe_load(File.read("frontend/pnpm-workspace.yaml"), aliases: false)
lock = YAML.safe_load(File.read("frontend/pnpm-lock.yaml"), aliases: false)
root = lock.fetch("importers").fetch(".")
packages = lock.fetch("packages")
snapshots = lock.fetch("snapshots")

def version(key, name)
  raise "wrong key: #{key}" unless key.start_with?("#{name}@")
  key.delete_prefix("#{name}@").split("(", 2).first.split(".").map(&:to_i)
end
def matching(table, name)
  table.keys.select { |key| key.start_with?("#{name}@") }
end

{
  "nanoid" => [3,3,19], "next" => [16,3,3], "sharp" => [0,35,4],
  "js-yaml" => [4,3,2], "vitest" => [4,1,11], "`@vitest/coverage-v8`" => [4,1,11],
}.each do |name, floor|
  ["packages", "snapshots"].each do |section|
    entries = matching(lock.fetch(section), name)
    raise "no #{section} entry for #{name}" if entries.empty?
    raise "below floor #{section} #{name}: #{entries}" if entries.any? { |key| (version(key, name) <=> floor) == -1 }
    puts "#{section} #{name}: #{entries.inspect}"
  end
end

%w[vitest `@vitest/coverage-v8`].each do |name|
  entry = root.fetch("devDependencies").fetch(name)
  declared = manifest.fetch("devDependencies").fetch(name)
  resolved = entry.fetch("version").to_s
  base = resolved.split("(", 2).first
  raise "#{name} specifier mismatch" unless entry.fetch("specifier") == declared
  raise "#{name} version mismatch" unless base == declared
  raise "#{name} missing package" unless packages.key?("#{name}@#{base}")
  raise "#{name} missing snapshot" unless snapshots.key?("#{name}@#{resolved}")
  puts "#{name}: importer/package/snapshot binding valid"
end

{"nanoid"=>"3.3.19", "sharp"=>"0.35.4", "js-yaml"=>"4.3.2"}.each do |name, expected|
  raise "#{name} workspace override" unless workspace.fetch("overrides").fetch(name).to_s == expected
  raise "#{name} lock override" unless lock.fetch("overrides").fetch(name).to_s == expected
  puts "#{name}: workspace and lock overrides valid"
end
RUBY

git diff --check '042b0c70531b229af3acbd0421a2f23098d848b3' '9d6d1e09e1ad52e57a1a3162c9984cceea4b2cb8'
printf 'diff_check=passed\n'

Length of output: 3339


@seonghobae I reviewed only develop@042b0c70531b229af3acbd0421a2f23098d848b3...9d6d1e09e1ad52e57a1a3162c9984cceea4b2cb8.

P1 — Test the Nano ID workspace override

backend/tests/test_frontend_nanoid_security.py, Line 14 validates the generated lockfile and the postcss@8.5.24 snapshot. It does not validate frontend/pnpm-workspace.yaml.

If a later change removes or changes overrides.nanoid, the current lockfile can still contain only nanoid@3.3.19. The test then passes. A subsequent lockfile regeneration can resolve a vulnerable Nano ID release. This is a false GREEN for the workspace policy added by this repair.

Load frontend/pnpm-workspace.yaml in this test. Assert that overrides.nanoid equals "3.3.19". Add a negative case for a missing or changed workspace override.

The current generated records are consistent:

  • The workspace and lockfile overrides specify Nano ID 3.3.19, sharp 0.35.4, and js-yaml 4.3.2.
  • Nano ID 3.3.19, Next.js 16.3.4, sharp 0.35.4, js-yaml 4.3.2, Vitest 4.1.11, and @vitest/coverage-v8 4.1.11 exist in both packages and snapshots.
  • The Vitest importer entries match frontend/package.json. Each entry references its base packages record and exact peer-qualified snapshots record.
  • git diff --check passed.

I applied the repository rule for pnpm v9 importer and peer-qualified snapshot identity during this review. I did not treat the central CodeQL gate or in-progress Strix gate as source findings.


🧠 Learnings used
Learnt from: seonghobae
Repo: ContextualWisdomLab/naruon PR: 1623
File: backend/tests/test_frontend_framework_security_floor.py:183-187
Timestamp: 2026-09-09T20:22:07.409Z
Learning: For pnpm v9 lockfiles in the frontend workspace, root package dependency records are stored at `importers["."]["devDependencies"]`, not `packages["."]`. Root importer `version` values can include peer-qualified suffixes, so tests must remove the suffix before comparing the resolved semantic version, while snapshot existence checks must use the exact peer-qualified version key.

You are interacting with an AI system.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working priority: high High-priority or P1 work

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant