Skip to content

perf(docs): split embedded manifest from payload - #3868

Closed
kimdogyeom wants to merge 1 commit into
Yeachan-Heo:devfrom
kimdogyeom:perf/docs-index-manifest-payload
Closed

perf(docs): split embedded manifest from payload#3868
kimdogyeom wants to merge 1 commit into
Yeachan-Heo:devfrom
kimdogyeom:perf/docs-index-manifest-payload

Conversation

@kimdogyeom

Copy link
Copy Markdown
Contributor

Summary

  • generate one canonical sorted docs manifest and one concatenated payload
  • list docs without payload reads and range-read only the selected document with bounds and integrity checks
  • keep public-sync, affected-CI, package, and compiled contracts closed over both fixed outputs

Verification

  • generator --check
  • focused lazy/search/public-sync/affected-CI/package tests
  • public-sync command and coding-agent package check
  • compiled empty-cwd protocol probe
  • quantitative manifest/load/package/binary gates

Copilot AI lite review requested due to automatic review settings August 5, 2026 11:01
@kimdogyeom

Copy link
Copy Markdown
Contributor Author

Quantitative rationale and gate evidence

Problem / impact / necessity

The previous generated TypeScript module coupled the sorted filename index to all 120 document bodies. Resolving the listing or one document therefore evaluated a 1,402,345-byte module and materialized the entire corpus. This is on a core protocol path and cannot be fixed by call-site caching because the payload and metadata were one import.

Solution

The canonical generator now emits exactly two fixed outputs: a sorted manifest (path, UTF-8 offset, length, sha256) and one concatenated binary payload. Listing consumes only manifest names. A document read validates safe bounds against the packaged payload, reads one slice, verifies its digest, and decodes only those bytes. Public-sync imports the canonical builder and compares both byte-for-byte; --check, affected-CI, and package gates cover either artifact and the generator.

Measurements

  • Former generated TS: 1,402,345 bytes
  • New manifest TS: 18,044 bytes (1.287%), below the 15% stop gate
  • Payload: 1,368,654 bytes
  • Listing payload bytes read: 0
  • One-document instrumentation: requested range only (test permits requested bytes +4 KiB and passed)
  • Packed paths: src/internal-urls/docs-index.generated.ts 18.0 KB and src/internal-urls/docs-payload.generated.bin 1.37 MB
  • Package dry run: 1,436 files, 19.22 MB unpacked; both exact paths asserted
  • Baseline compiled binary: 260,974,720 bytes
  • New compiled binary: 261,232,768 bytes
  • Growth: 258,048 bytes (0.099%), below 256 KiB and far below the governing larger 1% allowance

Compiled empty-CWD probe

A named temporary focused executable was compiled from the production protocol handler and run from /tmp/gjc-g007-empty-cwd outside the checkout:

{"listed":true,"document":"ERRATA-GPT5-HARMONY.md","bytes":9246,"contentType":"text/markdown"}

Passed gates

  • bun --cwd=packages/coding-agent run generate-docs-index --check
  • lazy listing/range/parity tests and internal-URL search tests
  • public-sync unit tests and bun run check:public-sync
  • affected-CI mapping tests, including payload-only selection
  • package-files dry-run contract test
  • bun --cwd=packages/coding-agent run check
  • compiled main-binary size comparison
  • compiled empty-CWD protocol probe

Sorted listing, byte-identical source parity, missing/suggestion/traversal behavior, exactly two generated outputs, stale/missing failure, payload-only CI selection, packed paths, and compiled operation remain covered. No quantitative or compiled/package threshold was waived.

Copilot AI 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.

Pull request overview

This PR improves gjc:// embedded docs performance by splitting the generated docs index into (1) a sorted manifest and (2) a concatenated binary payload, enabling listing without loading all doc bodies and enabling bounded range reads with integrity checks when fetching a single document.

Changes:

  • Generate and validate two fixed docs outputs: docs-index.generated.ts (manifest) + docs-payload.generated.bin (payload).
  • Update gjc:// protocol handling to range-read the payload with bounds + SHA-256 integrity validation.
  • Extend CI/packaging/public-sync gates and tests to treat generator + both outputs as one parity surface.

Reviewed changes

Copilot reviewed 9 out of 11 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
scripts/ci-dev-affected.ts Updates “embedded docs” affected-CI detection to include the generator and both fixed outputs.
scripts/ci-dev-affected.test.ts Tests that changes to generator/outputs correctly select the embedded-docs parity gate.
scripts/check-public-version-sync.ts Validates both generated docs outputs are present and current in public version sync checks.
scripts/check-public-version-sync.test.ts Updates fixtures and adds coverage for missing/stale payload output.
packages/coding-agent/test/package-files.test.ts Ensures the payload binary is included in the published package file set.
packages/coding-agent/test/docs-index-lazy.test.ts Adds tests proving listing doesn’t read payload and doc reads are range-bounded.
packages/coding-agent/src/internal-urls/gjc-protocol.ts Switches to manifest lookup + bounded payload slice reads + integrity checks for gjc://<doc>.
packages/coding-agent/scripts/generate-docs-index.ts Implements dual-output generator (index + payload) and --check mode.
packages/coding-agent/CHANGELOG.md Notes the embedded-docs lazy-loading performance fix.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +45 to +48
const encoder = new TextEncoder();
const documents = await Promise.all(
paths.map(async documentPath => encoder.encode(await Bun.file(path.join(docsDir, documentPath)).text())),
);
Comment on lines +101 to +102
const payload = Bun.file(EMBEDDED_DOCS_PAYLOAD_PATH);
const end = entry.offset + entry.length;
Comment on lines +221 to +224
if (
actual.byteLength !== expectedBytes.byteLength ||
!actual.every((byte, index) => byte === expectedBytes[index])
) {
@chatgpt-codex-connector

Copy link
Copy Markdown

💡 Codex Review

import docsPayloadPath from "./docs-payload.generated.bin" with { type: "file" };

P2 Badge Keep generated payload reachable in dev builds

When this generated file is compiled through packages/coding-agent/scripts/build-binary.ts, the build runs from packages/coding-agent with --root ../.. and package-relative entrypoints; in that context this type: "file" import resolves inside the binary to a missing /$bunfs/root/../../docs-payload...bin. Then Bun.file(...).size is 0, so any gjc://<doc>.md read from the local dist/gjc/dev-link binary throws Embedded documentation payload bounds are invalid instead of returning docs, even though the release-root compile path works.

ℹ️ 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".

@kimdogyeom

Copy link
Copy Markdown
Contributor Author

Refuted issuecomment-5190957348 on the exact PR head be3b53130bbf0c378c412ecb09bd4aaf0aae4714 with Bun 1.3.14 on linux-x64.

Production-path evidence:

  • Ran the actual dev production builder: bun packages/coding-agent/scripts/build-binary.ts (which invokes bun build --compile ... --root ../.. via buildDevCompileArgs). Build succeeded.
  • Built artifact: packages/coding-agent/dist/gjc, SHA-256 7bf78da49387b69b969aadd99e90d297516a038eab662cb622006327d47cbd91.
  • dist/gjc --version => gjc/0.12.12; dist/gjc --smoke-test => smoke-test: ok.
  • Started dist/gjc -p --mode=json --no-session --no-extensions --no-skills --no-rules --tools=read from a freshly created directory with zero initial entries.
  • The actual read tool call for gjc://tools/read.md returned the embedded document content (# read), with isError: false.
  • Boundary check: actual read calls for the first manifest entry gjc://ERRATA-GPT5-HARMONY.md (offset 0) and final entry gjc://ui-design-visual-qa.md (ending at payload byte 1,368,654) both returned content with isError: false; neither contained Embedded documentation payload bounds are invalid.
  • Worktree remained clean after generator/build verification.

Therefore the claimed /$bunfs/root/../../docs-payload...bin failure is not reproducible through the cited build-binary.ts/dev-build path at the exact reviewed head. No source change or speculative regression was added.

Listing embedded documentation previously loaded every document body through one generated TypeScript module. A canonical manifest plus one range-read payload keeps the protocol stable while bounding reads and preserving package and compiled behavior.

Lore-id: g007-docs-index-manifest-payload
Constraint: preserve sorted gjc:// listings and byte-identical document reads
Constraint: manifest must stay below 15% of the former generated module
Rejected: per-document generated payloads | creates orphan cleanup and package-surface risk
Confidence: high
Scope-risk: narrow
Reversibility: easy
Tested: generator parity, lazy range reads, public sync, affected CI, package files, package typecheck, compiled empty-cwd probe
Not-tested: full repository test suite
@Yeachan-Heo
Yeachan-Heo force-pushed the perf/docs-index-manifest-payload branch from be3b531 to ba6cad5 Compare August 6, 2026 11:51
@yazzang-homelab

Copy link
Copy Markdown
Contributor

경고 — 이 PR의 현재 head가 CHANGELOG 전체를 삭제한다

머지하면 안 된다. 확인된 사실:

$ git cat-file -s <이 PR head>:<해당 CHANGELOG 경로>
1

1바이트 — 개행 하나만 남았다. dev의 같은 파일은 312,259 bytes(coding-agent) / 244,785 bytes(ai) / 45,275 bytes(agent)다. 릴리스 이력 전체가 사라진 상태다.

원인은 내 쪽이다

#3932(11:25:32Z 머지)가 .gitattributes에서 packages/*/CHANGELOG.md merge=union을 제거했다. 제거 자체는 근거가 있었다 — union은 충돌을 내지 않고 양쪽을 이어붙여서 이미 릴리스된 섹션에 항목을 조용히 밀어넣고 있었다(#3929, 실측 35건).

그런데 그 결과 리베이스에서 CHANGELOG가 처음으로 진짜 충돌을 내기 시작했고, 그 충돌을 해소하는 과정에서 파일이 비워졌다. 시간대가 명확하다:

시각 (UTC) 사건
11:25:32 #3932 머지 (union 제거)
11:29:29 ~ 11:35:02 #3920 #3697 #3870 #3908 #3887 #3864 #3729 #3869 #3866 #3873작성자 6명, 10개 PR이 전부 1바이트 CHANGELOG로 갱신됨

전환 비용을 예고하지 못한 건 내 잘못이다. 미안하다.

복구

git fetch origin
git checkout origin/dev -- packages/coding-agent/CHANGELOG.md   # 해당 패키지 경로로
# 그 다음 ## [Unreleased] 아래에 이 PR의 항목만 다시 추가
git add packages/coding-agent/CHANGELOG.md
git commit --amend --no-edit    # 또는 새 커밋

앞으로 리베이스에서 CHANGELOG 충돌이 나면 양쪽 항목을 모두 ## [Unreleased] 아래에 남기는 것이 올바른 해소다. 이미 릴리스된 ## [X.Y.Z] 섹션은 손대지 않는다. CONTRIBUTING.md의 "Rebasing onto dev" 절에 적어두었다.

푸시 전에 다음으로 자가 점검할 수 있다:

git cat-file -s HEAD:packages/coding-agent/CHANGELOG.md   # 30만 바이트 근처여야 정상

@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

https://github.com/Yeachan-Heo/gajae-code/blob/ba6cad53cf468d36fdabc570175269eb17636c20/packages/coding-agent/src/internal-urls/docs-payload.generated.bin#L1
P1 Badge Regenerate the committed docs payload

The payload added by this commit does not match the docs corpus in the same tree: bun packages/coding-agent/scripts/generate-docs-index.ts --check reports docs-payload.generated.bin as stale. Consequently the public-sync gate rejects the commit, and pairing this asset with a freshly generated manifest can make gjc:// reads fail the new bounds or SHA-256 integrity checks. Regenerate and commit the payload from this exact tree.

AGENTS.md reference: AGENTS.md:L64-L70

ℹ️ 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".

"package.json": rootPackage(),
"packages/coding-agent/package.json": packageJson("@gajae-code/coding-agent"),
"packages/gajae-code/package.json": packageJson("gajae-code"),
<<<<<<< HEAD

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Resolve the committed conflict markers

The unresolved <<<<<<</=======/>>>>>>> block leaves this test file syntactically invalid, so bun test scripts/check-public-version-sync.test.ts fails during parsing before any tests run. Resolve the competing blocks and restore the missing test(...) wrapper.

Useful? React with 👍 / 👎.

- Updated Tavily missing-credential feedback to prompt users to configure an API-key provider setting instead of referencing `agent.db` directly
- Refreshed expired OpenAI code provider OAuth tokens during `web_search` execution and persisted the updated credentials so searches continue working after token expiry
- Wired `/login`, `/logout`, `/model`, and `/provider` TUI slash commands through interactive provider/model selectors and existing OAuth flows.
- The package no longer advertises `./extensibility/custom-commands/bundled/review`. That module was deleted when the bu

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Restore the released changelog history

This edit truncates the final retained bullet mid-word and deletes roughly 1,400 subsequent lines, including released version sections such as 0.11.11; publishing this package would permanently remove users' historical release notes even though this docs-payload change has no reason to rewrite them. Restore the deleted released sections and add any new note only under Unreleased.

AGENTS.md reference: AGENTS.md:L178-L178

Useful? React with 👍 / 👎.

@yazzang-homelab yazzang-homelab left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Independent architect review, on head ba6cad53.

1. Blocking — the CHANGELOG lost 247 KB

packages/coding-agent/CHANGELOG.md on this head is 65,537 bytes; dev has 312,259. Roughly 1,439 lines of released history are gone.

This one is on me, not you. #3932 (which I authored, merged 11:25:32Z) removed packages/*/CHANGELOG.md merge=union, and within ten minutes twelve open PRs across six authors force-pushed heads with damaged changelogs. Full writeup and the recovery command in #3942; #3941 adds the CI guard that should have shipped with the original change.

git checkout origin/dev -- packages/coding-agent/CHANGELOG.md
# then re-add only this PR's entry under ## [Unreleased]

2. The reconciliation with #3932 is mostly done — one gap left

You have already rebased onto the merged #3932: this head carries isTrackedByGit, keeps the GENERATED_DOCS_INDEX tracked-guard at line 250, and adds GENERATED_DOCS_PAYLOAD. Good — that was the hard part.

The gap: docs-payload.generated.bin is tracked, and nothing guards it.

$ git ls-tree <head> packages/coding-agent/src/internal-urls/docs-payload.generated.bin
100644 blob ee4f0044...        # 1,368,654 bytes, 21,706 lines

$ grep docs-payload <head>:.gitignore
(nothing — only docs-index.generated.ts at line 78)

$ grep -n isTrackedByGit <head>:scripts/check-public-version-sync.ts
250:  if (await isTrackedByGit(repoRoot, GENERATED_DOCS_INDEX)) {   # payload not checked

So this PR removes one tracked 1.4 MB generated artifact and adds a different tracked 1.4 MB generated artifact, while the guard that was added to stop exactly that keeps pointing at the old filename. After this lands, the guard is technically still green and materially a no-op.

Pick one and be explicit about it:

  • Untrack the payload — add it to .gitignore, generate it in prepare/prepack like the index, and extend the isTrackedByGit check to cover GENERATED_DOCS_PAYLOAD. This matches the precedent #3932 set and is what I would do.
  • Keep it tracked deliberately — then say why in the PR body (range-reads need the file present without a build step, presumably), and delete or retarget the now-vacuous isTrackedByGit check so it does not read as protection it no longer provides.

Either is defensible. Silently having both is not.

3. Credit where due: the new format is genuinely mergeable

This is the part I did not expect and it materially changes my view of the design. The old artifact put an entire document on one source line, which git cannot three-way merge — that single property is what made it 6 of the 10 real conflicts among open PRs. The payload is different:

21,706 lines, max line length 1,934 chars

Line-oriented, with no pathological line. Two branches editing different docs merge cleanly; two branches editing the same doc conflict in a readable hunk. So even if you keep it tracked, you are not reintroducing the conflict class — only the repo-size churn. That is a much weaker objection than the one #3932 was answering, and it should be stated in the PR body, because "adds a tracked 1.4 MB generated file" reads far worse than it is.

Manifest/payload split with bounds and integrity checks on the range read is also the right shape: listing docs without touching the payload is the actual win, and checking integrity at read time rather than trusting the offset table is the correct paranoia for a binary side-file.

4. Remaining conflicts

merge-tree against current dev still reports four:

packages/coding-agent/src/internal-urls/docs-index.generated.ts
packages/coding-agent/test/docs-index-lazy.test.ts
scripts/check-public-version-sync.test.ts
scripts/check-public-version-sync.ts

The first resolves to "deleted on both sides". The other three are your changes against mine in the same regions — mechanical, but please re-run bun test scripts/check-public-version-sync.test.ts packages/coding-agent/test/docs-index-lazy.test.ts after resolving, since both files now contain assertions from two different changes with overlapping intent.

Restore the changelog, resolve the payload-tracking question, and I will do a full pass on the range-read logic against the new head.

gajae.pr-review-verdict.v1 merge-blocked sha256:ba6cad53cf468d36fdabc570175269eb17636c20 reviewer:architect evidence:git ls-tree/cat-file on this head — CHANGELOG.md 65537 vs 312259 on dev; docs-payload.generated.bin tracked at 1368654 bytes/21706 lines with no .gitignore entry and no isTrackedByGit guard; merge-tree vs origin/dev reports 4 conflicts

@Yeachan-Heo

Copy link
Copy Markdown
Owner

Closing during the emergency maintenance freeze. This PR is not in the retained critical or maintainer-owned set. Do not open a replacement PR unless a maintainer explicitly directs it.


[repo owner's gaebal-gajae (clawdbot) 🦞]

@yazzang-homelab

Copy link
Copy Markdown
Contributor

CHANGELOG 항목이 이미 릴리스된 섹션에 들어가 있다.

dev의 릴리스 구간과 이 PR head의 릴리스 구간을 비교해, 이 PR이 추가한 줄만 뽑았다(coding-agent). ## [Unreleased]가 아니라 이미 배포된 ## [X.Y.Z] 아래에 있다.

원인은 .gitattributespackages/*/CHANGELOG.md merge=union이다(오늘 11:25:32Z에 #3932로 제거됨). union은 충돌을 내지 않고 양쪽 청크를 이어붙이는데, 릴리스 커밋이 ## [X.Y.Z]를 살아남은 ## [Unreleased] 바로 아래에 삽입하기 때문에 Unreleased에 넣은 항목이 리베이스에서 새 버전 헤딩 밑으로 조용히 옮겨진다. 충돌 마커도 CI 신호도 없다. dev에서 같은 상태인 기존 항목 35건, 열린 PR 8건을 확인했다(#3929).

리베이스로는 안 풀린다 — 위치가 이미 커밋돼 있어서 직접 옮겨야 한다. 해당 줄을 잘라 ## [Unreleased] 아래로 넣으면 된다. 드라이버는 제거됐으니 한 번 옮기면 다시 움직이지 않는다.

내 PR(#3844)도 같은 상태였고 방금 고쳤다.

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.

4 participants