Skip to content

Fix knowledge graph progress output#92

Merged
Moskize91 merged 7 commits into
mainfrom
codex/cleanup-staging-workspaces
Jul 4, 2026
Merged

Fix knowledge graph progress output#92
Moskize91 merged 7 commits into
mainfrom
codex/cleanup-staging-workspaces

Conversation

@Moskize91

Copy link
Copy Markdown
Contributor

Summary

  • hide the invalid knowledge graph words counter from job progress snapshots
  • make progress status output render generic metric groups instead of hard-coded token fields
  • add regression coverage for knowledge graph progress counters

Tests

  • pnpm exec tsc -p tsconfig.json --noEmit
  • pnpm lint
  • pnpm format:check
  • pnpm test:run

@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown

Review Change Stack

Summary by CodeRabbit

  • New Features

    • Improved build-job status output with clearer progress phases and metrics.
    • Added support for retaining job cache and log paths in job details and cleanup behavior.
  • Bug Fixes

    • Garbage collection now removes completed-job logs, caches, and empty workspace descendants more reliably.
    • Logging output now uses a clearer run log file name.
  • Documentation

    • Updated help text to better describe runtime queue storage and cleanup behavior.

Walkthrough

This PR replaces fragment-based topology grouping, chunk retrieval, and chapter-summary logic with sentence-start-index/segment-based equivalents. The build queue gains per-job cache and log directories persisted in schema and used across CLI, progress reporting, and GC. Progress output introduces metric groups replacing token-specific fields, with a new "committing" phase. LLM request logging is centralized via new formatting helpers. WIKG archives now embed and validate a mutation token, and the coordinator can adopt cached search-index overlays across archives when mutation tokens match, alongside expanded GC cleanup of descendant directories.

Sequence Diagram(s)

sequenceDiagram
  participant CLI as cli/queue.ts
  participant Reporter as ProgressReporter
  participant Archive as Archive write

  CLI->>Reporter: updatePhase(committing, done=0)
  CLI->>Archive: commit artifact
  Archive-->>CLI: write complete
  CLI->>Reporter: updatePhase(committing, done=1)
Loading
sequenceDiagram
  participant Coordinator as wikg-coordinator
  participant State as entry_overlays (state DB)
  participant Archive as Target archive workspace

  Coordinator->>State: listSearchIndexAdoptionCandidates(mutationToken)
  State-->>Coordinator: matching candidate overlays
  Coordinator->>Coordinator: validate eligibility (locks, mutation token, no conflicts)
  Coordinator->>Archive: move candidate sqlite file into target workspace
  Coordinator->>State: update entry_overlays with new path
Loading

Compact metadata

  • Type: Refactor + Feature
  • Scope: Topology grouping, build queue persistence, progress output, LLM logging, WIKG archive format
  • Estimated review effort: High (touches core data flow, storage schema, and archive format across many files)

Related issues: None referenced in provided data.

Related PRs: None referenced in provided data.

Suggested labels: refactor, feature, storage-schema, needs-careful-review

Suggested reviewers: Reviewers familiar with topology/grouping internals, build-queue persistence, and WIKG archive format.

Poem
A rabbit hopped from fragment to phrase,
Segments now light the sentence maze.
Cache and log paths, tucked snug in their den,
Mutation tokens guard the archive again.
Committing phases blink, hop, and cheer—
The build queue burrow grows sturdier this year. 🐇

🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title describes the change, but it does not follow the required <type>(<scope>): <subject> format. Rewrite it as something like fix(knowledge-graph): improve progress output to satisfy the required format.
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The description is clearly related to the progress output and regression coverage changes.
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.
✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch codex/cleanup-staging-workspaces

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

@Moskize91 Moskize91 merged commit 9e7ae5d into main Jul 4, 2026
2 of 3 checks passed
@Moskize91 Moskize91 deleted the codex/cleanup-staging-workspaces branch July 4, 2026 02:27

@coderabbitai coderabbitai 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.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/facade/build-queue.ts (1)

199-211: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Add a migration for build_jobs. CREATE TABLE IF NOT EXISTS won’t add cache_path/log_path to an existing queue DB, so upgraded installs will still hit missing-column failures in mapBuildJob. Add an ALTER TABLE/backfill step for existing rows.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/facade/build-queue.ts` around lines 199 - 211, The build queue schema
update for build_jobs is incomplete because CREATE TABLE IF NOT EXISTS will not
add the new cache_path and log_path columns to existing databases. Update the
build-queue initialization/migration logic around BUILD_QUEUE_SCHEMA_SQL and
mapBuildJob to detect older queue DBs, run an ALTER TABLE/backfill step for
build_jobs, and ensure existing rows get valid values before they are read.
src/wikg/wikg-coordinator.ts (1)

597-627: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Dry-run should include descendant-directory counts. --dry-run is user-facing here, so the GC report should still scan and count disposable descendant directories like the file-removal loop above; gating the whole removeDisposableDescendantDirectories() call on !context.dryRun makes dry runs underreport scanned/removed/freedBytes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/wikg/wikg-coordinator.ts` around lines 597 - 627, The dry-run path in the
GC flow is skipping descendant-directory accounting, so the report undercounts
scanned, removed, and freedBytes. Update the archive cleanup logic in
wikg-coordinator’s workspace scan to always call
removeDisposableDescendantDirectories for counting purposes, and only gate the
actual destructive deletion behavior on context.dryRun, matching the
file-removal loop’s dry-run handling.
🧹 Nitpick comments (5)
test/topology/grouping.test.ts (1)

61-86: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Duplicated fixture helper across test files.

createSerialFragments here is nearly identical to the one in test/topology/segment-incision.test.ts (lines 167-192), and a similar inline fixture exists in test/topology/topology.test.ts. Consider extracting to a shared test util to avoid drift as the segment-based fixture evolves.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/topology/grouping.test.ts` around lines 61 - 86, The
createSerialFragments test fixture is duplicated across topology test files, so
update the grouping.test.ts helper to use a shared reusable test utility instead
of maintaining a near-identical local copy. Extract the common fixture logic
from createSerialFragments into a shared topology test helper that can be
imported by grouping.test.ts, segment-incision.test.ts, and the similar inline
setup in topology.test.ts, keeping the same ReadonlySerialFragments shape and
behavior.
src/llm/client.ts (2)

365-376: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Redundant error logging across [[Error]] and [[Error Stack]] blocks.

Both the payment-required and non-retryable branches now append a [[Error]]: block via formatError(...) and immediately follow it with formatRequestResultLog(tokenUsage, error), which appends its own [[Error Stack]]: block for the same error. This isn't a bug, but it duplicates error context (message chain vs. stack trace) in every failure log. Consider dropping the standalone [[Error]]: append and relying solely on formatRequestResultLog's usage+error-stack composition, or vice versa, to keep logs concise.

♻️ Example consolidation
-          await requestLog.append(
-            `[[Error]]:\n${formatError(paymentError)}\n\n`,
-          );
-          await requestLog.append(formatRequestResultLog(tokenUsage, error));
+          await requestLog.append(formatRequestResultLog(tokenUsage, paymentError));
           throw paymentError;

Also applies to: 609-617

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/llm/client.ts` around lines 365 - 376, The failure paths in
`src/llm/client.ts` are logging the same error twice: once through the
standalone `[[Error]]` append and again inside
`formatRequestResultLog(tokenUsage, error)` via `[[Error Stack]]`. Update the
payment-required and non-retryable branches to keep only one error
representation, using either the direct `formatError(...)` append or the
`formatRequestResultLog` output, and apply the same cleanup to the other
matching block referenced in the comment.

609-651: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider a more type-safe usage/cache-hit representation.

formatRequestUsageLog/formatRequestResultLog mix LanguageModelUsage with a "cache-hit" string sentinel in the same parameter type. A small discriminated shape (e.g., { kind: "usage"; usage: LanguageModelUsage } | { kind: "cache-hit" }) or a dedicated formatCacheHitResultLog() helper would avoid the stringly-typed branch and make the API surface clearer, though the current approach works correctly.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/llm/client.ts` around lines 609 - 651, The request/result logging helpers
use a string sentinel mixed into the `LanguageModelUsage` type, which makes the
API less type-safe. Update `formatRequestResultLog` and `formatRequestUsageLog`
to use a discriminated result shape or split out a dedicated
`formatCacheHitResultLog` helper, and adjust the `"cache-hit"` branch
accordingly while keeping `formatRequestErrorStackLog` and `formatTokenCount`
behavior unchanged.
src/wikg/wikg-coordinator.ts (1)

1748-1774: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Consider an index on mutation_token if this table grows.

The lookup filters on mutation_token with no supporting index (only PK is archive_key, entry_path), so this is a full-table scan. Likely fine at expected entry_overlays scale, but worth an index if the number of tracked archives/overlays grows significantly.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/wikg/wikg-coordinator.ts` around lines 1748 - 1774, The query in
listSearchIndexAdoptionCandidates filters by mutation_token and entry_path while
the existing primary key on entry_overlays does not support that lookup, so add
an index that matches the WHERE clauses used here. Update the schema/migration
for entry_overlays to include an index on mutation_token, ideally combined with
entry_path and/or archive_key if that better fits the query pattern, and ensure
any database setup code that defines the table stays consistent with the new
index.
src/topology/segment-incision.ts (1)

180-194: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Use binary search for segment containment lookups.

Line 186 scans every segment start for each chunk and external edge endpoint; this makes the incision pass scale with segments × (chunks + edge visits). Since startSentenceIndexes is already sorted, a binary search keeps the same behavior with much lower cost.

⚡ Proposed lookup optimization
 function findContainingSegmentStartIndex(
   startSentenceIndexes: readonly number[],
   sentenceIndex: number,
 ): number | undefined {
-  let containingSegmentStartIndex: number | undefined;
-
-  for (const startSentenceIndex of startSentenceIndexes) {
-    if (startSentenceIndex > sentenceIndex) {
-      break;
-    }
-
-    containingSegmentStartIndex = startSentenceIndex;
-  }
-
-  return containingSegmentStartIndex;
+  let low = 0;
+  let high = startSentenceIndexes.length - 1;
+  let containingSegmentStartIndex: number | undefined;
+
+  while (low <= high) {
+    const mid = Math.floor((low + high) / 2);
+    const startSentenceIndex = startSentenceIndexes[mid];
+
+    if (startSentenceIndex === undefined) {
+      break;
+    }
+    if (startSentenceIndex > sentenceIndex) {
+      high = mid - 1;
+      continue;
+    }
+
+    containingSegmentStartIndex = startSentenceIndex;
+    low = mid + 1;
+  }
+
+  return containingSegmentStartIndex;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/topology/segment-incision.ts` around lines 180 - 194, The containment
lookup in findContainingSegmentStartIndex is doing a linear scan over
already-sorted startSentenceIndexes for every sentenceIndex, which makes segment
incision scale poorly. Replace the loop with a binary search that returns the
greatest startSentenceIndex less than or equal to sentenceIndex, preserving the
current undefined behavior when no segment starts before the target. Keep the
change localized to findContainingSegmentStartIndex so the callers in the
incision pass continue to work unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/document/stores.ts`:
- Around line 817-824: The persistent implementation of
listBySentenceStartIndexes is only querying a single sentence index, which makes
it diverge from the snapshot store’s full-fragment behavior. Update
listBySentenceStartIndexes to expand each selected start through the full
segment range, using listBySentenceRange consistently so the DB-backed path
returns the same chunk boundaries as the snapshot store. Keep the fix localized
around listBySentenceStartIndexes and its call to listBySentenceRange.

In `@src/facade/build-queue.ts`:
- Around line 280-283: Validate the explicit jobId before it is passed into
createJobWorkspacePath, createJobCachePath, and createJobLogPath in
build-queue.ts, because options.jobId is used directly in filesystem paths and
later removed recursively. Add a guard in the job setup flow near the jobId
assignment to reject any provided ID containing path separators or dot segments
like . and .., while still allowing randomUUID-generated IDs to proceed
unchanged.

In `@src/topology/resource-segmentation.ts`:
- Around line 30-35: The sentence range calculation in resource-segmentation is
using the last segment start instead of the full segment boundary, which can
truncate multi-sentence groups. Update the grouping logic in SentenceGroupRecord
creation so the endSentenceIndex comes from the segment’s true end boundary,
either by carrying that boundary through SegmentInfo or computing it before
mapping groups in the resource-segmentation flow. Make sure the fix is applied
in the code path that builds SentenceGroupRecord objects from groups.

In `@src/wikg/archive.ts`:
- Around line 26-36: `validateArchiveMutationToken()` now hard-rejects archives
whose first entry is not `.wikg-mutation-token`, which breaks older token-less
archives without any compatibility path. Update the archive format handling in
`src/wikg/archive.ts` by either bumping `WIKG_FORMAT_VERSION` and treating this
as a deliberate format change, or adding a migration/backfill path in
`validateArchiveMutationToken()`/related archive-reading logic so pre-token
`.wikg` archives remain readable.

In `@src/wikg/wikg-coordinator.ts`:
- Around line 883-895: The search-index fallback in wikg-coordinator’s
archive-read path should tolerate missing mutation tokens in legacy or external
archives. In the block that calls tryAdoptSearchIndexCacheOverlay and then
re-reads the overlay, wrap the awaited adoption helper in a catch that ignores
adoption failures so readWikgArchiveMutationToken cannot abort resolution for
otherwise readable archives; keep the fallback behavior gated by
SEARCH_INDEX_DATABASE_ENTRY_PATH and the overlay file-kind check.

---

Outside diff comments:
In `@src/facade/build-queue.ts`:
- Around line 199-211: The build queue schema update for build_jobs is
incomplete because CREATE TABLE IF NOT EXISTS will not add the new cache_path
and log_path columns to existing databases. Update the build-queue
initialization/migration logic around BUILD_QUEUE_SCHEMA_SQL and mapBuildJob to
detect older queue DBs, run an ALTER TABLE/backfill step for build_jobs, and
ensure existing rows get valid values before they are read.

In `@src/wikg/wikg-coordinator.ts`:
- Around line 597-627: The dry-run path in the GC flow is skipping
descendant-directory accounting, so the report undercounts scanned, removed, and
freedBytes. Update the archive cleanup logic in wikg-coordinator’s workspace
scan to always call removeDisposableDescendantDirectories for counting purposes,
and only gate the actual destructive deletion behavior on context.dryRun,
matching the file-removal loop’s dry-run handling.

---

Nitpick comments:
In `@src/llm/client.ts`:
- Around line 365-376: The failure paths in `src/llm/client.ts` are logging the
same error twice: once through the standalone `[[Error]]` append and again
inside `formatRequestResultLog(tokenUsage, error)` via `[[Error Stack]]`. Update
the payment-required and non-retryable branches to keep only one error
representation, using either the direct `formatError(...)` append or the
`formatRequestResultLog` output, and apply the same cleanup to the other
matching block referenced in the comment.
- Around line 609-651: The request/result logging helpers use a string sentinel
mixed into the `LanguageModelUsage` type, which makes the API less type-safe.
Update `formatRequestResultLog` and `formatRequestUsageLog` to use a
discriminated result shape or split out a dedicated `formatCacheHitResultLog`
helper, and adjust the `"cache-hit"` branch accordingly while keeping
`formatRequestErrorStackLog` and `formatTokenCount` behavior unchanged.

In `@src/topology/segment-incision.ts`:
- Around line 180-194: The containment lookup in findContainingSegmentStartIndex
is doing a linear scan over already-sorted startSentenceIndexes for every
sentenceIndex, which makes segment incision scale poorly. Replace the loop with
a binary search that returns the greatest startSentenceIndex less than or equal
to sentenceIndex, preserving the current undefined behavior when no segment
starts before the target. Keep the change localized to
findContainingSegmentStartIndex so the callers in the incision pass continue to
work unchanged.

In `@src/wikg/wikg-coordinator.ts`:
- Around line 1748-1774: The query in listSearchIndexAdoptionCandidates filters
by mutation_token and entry_path while the existing primary key on
entry_overlays does not support that lookup, so add an index that matches the
WHERE clauses used here. Update the schema/migration for entry_overlays to
include an index on mutation_token, ideally combined with entry_path and/or
archive_key if that better fits the query pattern, and ensure any database setup
code that defines the table stays consistent with the new index.

In `@test/topology/grouping.test.ts`:
- Around line 61-86: The createSerialFragments test fixture is duplicated across
topology test files, so update the grouping.test.ts helper to use a shared
reusable test utility instead of maintaining a near-identical local copy.
Extract the common fixture logic from createSerialFragments into a shared
topology test helper that can be imported by grouping.test.ts,
segment-incision.test.ts, and the similar inline setup in topology.test.ts,
keeping the same ReadonlySerialFragments shape and behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3ab5cddb-26d1-4348-9303-cd268016aa82

📥 Commits

Reviewing files that changed from the base of the PR and between 2466632 and 95d482a.

📒 Files selected for processing (31)
  • data/help/commands/gc.jinja
  • data/help/topics/runtime.jinja
  • src/archive/query/archive-view.ts
  • src/cli/progress-output.ts
  • src/cli/queue.ts
  • src/cli/stage-runtime.ts
  • src/common/logging.ts
  • src/document/stores.ts
  • src/facade/build-queue.ts
  • src/facade/chapter-build.ts
  • src/gc/files.ts
  • src/llm/client.ts
  • src/topology/grouping.ts
  • src/topology/resource-segmentation.ts
  • src/topology/segment-incision.ts
  • src/topology/topology.ts
  • src/wikg/archive.ts
  • src/wikg/wikg-coordinator.ts
  • test/cli/queue.test.ts
  • test/common/logging.test.ts
  • test/document/stores.test.ts
  • test/editor/markup.test.ts
  • test/facade/build-queue.test.ts
  • test/gc/gc.test.ts
  • test/llm/client.test.ts
  • test/topology/grouping.test.ts
  • test/topology/resource-segmentation.test.ts
  • test/topology/segment-incision.test.ts
  • test/topology/topology.test.ts
  • test/wikg/archive.test.ts
  • test/wikg/spine-digest-file.test.ts

Comment thread src/document/stores.ts
Comment on lines 817 to +824
const results = await Promise.all(
fragmentIds.map(
async (fragmentId) =>
await this.listBySentenceRange(serialId, fragmentId, fragmentId),
sentenceStartIndexes.map(
async (sentenceStartIndex) =>
await this.listBySentenceRange(
serialId,
sentenceStartIndex,
sentenceStartIndex,
),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify all callers and implementations agree on range semantics.
rg -n -C3 '\blistBySentenceStartIndexes\s*\(' src test

Repository: oomol-lab/wiki-graph

Length of output: 2473


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the interface, persistent implementation, snapshot implementation, and tests
sed -n '1,120p' src/document/stores.ts
printf '\n--- PERSISTENT IMPLEMENTATION ---\n'
sed -n '780,860p' src/document/stores.ts
printf '\n--- SNAPSHOT IMPLEMENTATION ---\n'
sed -n '930,990p' src/facade/chapter-build.ts
printf '\n--- TESTS ---\n'
sed -n '280,340p' test/document/stores.test.ts

Repository: oomol-lab/wiki-graph

Length of output: 9602


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect chunk/sentence model and the segment-range helper.
rg -n -C4 'createSegmentRanges|sentenceIds|sentenceId|sentence_index|listBySentenceStartIndexes' src test

Repository: oomol-lab/wiki-graph

Length of output: 50377


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the segment-range helper and any tests covering snapshot semantics.
rg -n -C4 'function createSegmentRanges|createSegmentRanges\(|listBySentenceStartIndexes\(|segmentRanges' src test

Repository: oomol-lab/wiki-graph

Length of output: 4452


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read the segment-range helper definition.
sed -n '1372,1428p' src/facade/chapter-build.ts

Repository: oomol-lab/wiki-graph

Length of output: 1554


Make listBySentenceStartIndexes return the whole fragment range in the persistent store. The DB-backed implementation only matches sentence_index = start, while the snapshot store expands each selected start to its full segment range. That can return different chunks for the same selection; align the persistent path with the same range semantics.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/document/stores.ts` around lines 817 - 824, The persistent implementation
of listBySentenceStartIndexes is only querying a single sentence index, which
makes it diverge from the snapshot store’s full-fragment behavior. Update
listBySentenceStartIndexes to expand each selected start through the full
segment range, using listBySentenceRange consistently so the DB-backed path
returns the same chunk boundaries as the snapshot store. Keep the fix localized
around listBySentenceStartIndexes and its call to listBySentenceRange.

Comment thread src/facade/build-queue.ts
Comment on lines 280 to +283
const jobId = options.jobId ?? randomUUID();
const workspacePath = await createJobWorkspacePath(archiveKey, jobId);
const workspacePath = await createJobWorkspacePath(jobId);
const cachePath = await createJobCachePath(jobId);
const logPath = await createJobLogPath(jobId);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== build-queue outline ==\n'
ast-grep outline src/facade/build-queue.ts --view expanded || true

printf '\n== jobId-related search ==\n'
rg -n "jobId|createJobWorkspacePath|createJobCachePath|createJobLogPath|rm\\(|remove|recursive" src/facade/build-queue.ts src -g '!**/dist/**' -g '!**/build/**' || true

Repository: oomol-lab/wiki-graph

Length of output: 47197


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1490,1545p' src/facade/build-queue.ts
printf '\n== rm call sites around job cleanup ==\n'
sed -n '920,990p' src/facade/build-queue.ts
printf '\n== disposable child dir helper ==\n'
sed -n '1,220p' src/gc/files.ts

Repository: oomol-lab/wiki-graph

Length of output: 8172


Validate jobId before using it in filesystem paths. options.jobId is joined directly into workspace/cache/log/events paths, and those paths are later removed recursively; reject separators and ./.. for explicit IDs to prevent path traversal through this exported API.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/facade/build-queue.ts` around lines 280 - 283, Validate the explicit
jobId before it is passed into createJobWorkspacePath, createJobCachePath, and
createJobLogPath in build-queue.ts, because options.jobId is used directly in
filesystem paths and later removed recursively. Add a guard in the job setup
flow near the jobId assignment to reject any provided ID containing path
separators or dot segments like . and .., while still allowing
randomUUID-generated IDs to proceed unchanged.

Comment on lines +30 to 35
return groups.map((startSentenceIndexes, groupId) => ({
endSentenceIndex: Math.max(...startSentenceIndexes),
groupId,
serialId: input.serialId,
startSentenceIndex: Math.min(...fragmentIds),
startSentenceIndex: Math.min(...startSentenceIndexes),
}));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== file list ==\n'
git ls-files 'src/topology/*' | sed -n '1,200p'

printf '\n== outline: src/topology/resource-segmentation.ts ==\n'
ast-grep outline src/topology/resource-segmentation.ts --view expanded || true

printf '\n== surrounding source (numbered) ==\n'
cat -n src/topology/resource-segmentation.ts | sed -n '1,220p'

printf '\n== search for related types/usages ==\n'
rg -n "SentenceGroupRecord|SegmentInfo|createSegmentGroups|startSentenceIndex|endSentenceIndex|sentence range|segment boundary" src/topology src -g '!**/node_modules/**' || true

Repository: oomol-lab/wiki-graph

Length of output: 31523


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== segment-incision.ts ==\n'
cat -n src/topology/segment-incision.ts | sed -n '1,260p'

printf '\n== grouping.ts ==\n'
cat -n src/topology/grouping.ts | sed -n '1,120p'

printf '\n== SentenceGroupRecord definition ==\n'
cat -n src/document/types.ts | sed -n '135,155p'

printf '\n== downstream group consumers ==\n'
sed -n '250,330p' src/editor/editor.ts
printf '\n---\n'
sed -n '1150,1425p' src/facade/chapter-build.ts

Repository: oomol-lab/wiki-graph

Length of output: 20531


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== listGroupSentences body ==\n'
sed -n '330,390p' src/facade/chapter-build.ts

printf '\n== any direct range clipping against SentenceGroupRecord ==\n'
rg -n "group\.endSentenceIndex|group\.startSentenceIndex|SentenceGroupRecord" src/facade/chapter-build.ts src/editor/editor.ts src/topology -A3 -B3

Repository: oomol-lab/wiki-graph

Length of output: 8273


Use the last segment’s sentence boundary here. Math.max(...startSentenceIndexes) only returns the final segment start, so multi-sentence segments get truncated and downstream sentence-range lookups can miss trailing sentences. Carry the segment end boundary through SegmentInfo or derive it before emitting SentenceGroupRecord. src/topology/resource-segmentation.ts:30-35

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/topology/resource-segmentation.ts` around lines 30 - 35, The sentence
range calculation in resource-segmentation is using the last segment start
instead of the full segment boundary, which can truncate multi-sentence groups.
Update the grouping logic in SentenceGroupRecord creation so the
endSentenceIndex comes from the segment’s true end boundary, either by carrying
that boundary through SegmentInfo or computing it before mapping groups in the
resource-segmentation flow. Make sure the fix is applied in the code path that
builds SentenceGroupRecord objects from groups.

Comment thread src/wikg/archive.ts
Comment on lines +26 to +36
const WIKG_MUTATION_TOKEN_PATH = ".wikg-mutation-token";
const WIKG_MANIFEST_PATH = "manifest.json";
const SEARCH_INDEX_DATABASE_PATH = "fts.db";
const WIKG_MANIFEST_CONTENT = `${JSON.stringify({
formatVersion: WIKG_FORMAT_VERSION,
})}\n`;
const WIKG_MUTATION_TOKEN_MAGIC = "wikg-mutation-token:v1";
const WIKG_MUTATION_TOKEN_PATTERN = /^[A-Za-z0-9_-]{43}$/u;

const WIKG_ARCHIVE_PATTERNS = [
/^\.wikg-mutation-token$/u,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Look for any migration/backfill logic tied to mutation tokens or format version bumps.
rg -n "WIKG_FORMAT_VERSION|migrat|backfill|legacy" src/wikg -C3
rg -n "readWikgArchiveMutationToken|validateArchiveMutationToken" src -C3

Repository: oomol-lab/wiki-graph

Length of output: 4540


🏁 Script executed:

#!/bin/bash
sed -n '1,220p' src/wikg/archive.ts
printf '\n---\n'
sed -n '220,620p' src/wikg/archive.ts
printf '\n---\n'
sed -n '1580,1675p' src/wikg/wikg-coordinator.ts

Repository: oomol-lab/wiki-graph

Length of output: 18628


🏁 Script executed:

#!/bin/bash
rg -n "Unsupported WIKG format version|Missing WIKG mutation token|formatVersion|mutation token" src test tests -C3

Repository: oomol-lab/wiki-graph

Length of output: 8071


Add a version gate or migration for token-less archives.

validateArchiveMutationToken() now rejects any archive whose first entry is not .wikg-mutation-token, but WIKG_FORMAT_VERSION stays at 1 and there’s no fallback for older archives. That makes pre-token .wikg files unreadable on upgrade; bump the format version or add a migration/backfill path so the compatibility break is explicit.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/wikg/archive.ts` around lines 26 - 36, `validateArchiveMutationToken()`
now hard-rejects archives whose first entry is not `.wikg-mutation-token`, which
breaks older token-less archives without any compatibility path. Update the
archive format handling in `src/wikg/archive.ts` by either bumping
`WIKG_FORMAT_VERSION` and treating this as a deliberate format change, or adding
a migration/backfill path in `validateArchiveMutationToken()`/related
archive-reading logic so pre-token `.wikg` archives remain readable.

Comment on lines 883 to +895
let overlay = await this.#readOverlay(entryPath);

if (
entryPath === SEARCH_INDEX_DATABASE_ENTRY_PATH &&
overlay?.kind !== "file"
) {
await tryAdoptSearchIndexCacheOverlay({
targetArchiveKey: this.#archiveKey,
targetArchivePath: this.#archivePath,
});
overlay = await this.#readOverlay(entryPath);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

rg -n "mutation token|readWikgArchiveMutationToken|writeWikgArchiveMutationToken|WIKG mutation token|archive" src/wikg/archive.ts src/wikg -g '!**/dist/**'

Repository: oomol-lab/wiki-graph

Length of output: 27607


🏁 Script executed:

sed -n '150,260p' src/wikg/archive.ts

Repository: oomol-lab/wiki-graph

Length of output: 2826


🏁 Script executed:

sed -n '260,420p' src/wikg/archive.ts

Repository: oomol-lab/wiki-graph

Length of output: 4348


🏁 Script executed:

sed -n '1,150p' src/wikg/archive.ts

Repository: oomol-lab/wiki-graph

Length of output: 4245


🏁 Script executed:

rg -n "WIKG_FORMAT_VERSION|formatVersion|mutation token|readWikgArchiveMutationToken|createWikgMutationTokenContent|writeWikgArchiveWithOverlays|extractWikgArchive" src

Repository: oomol-lab/wiki-graph

Length of output: 2133


Guard the token-adoption fallback
writeWikgArchive* always adds .wikg-mutation-token, but readWikgArchiveMutationToken still throws when that entry is missing. Legacy or externally produced archives can legitimately lack the token, so this awaited helper can still abort search-index resolution even though the archive is otherwise readable. Catch and ignore adoption failures here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/wikg/wikg-coordinator.ts` around lines 883 - 895, The search-index
fallback in wikg-coordinator’s archive-read path should tolerate missing
mutation tokens in legacy or external archives. In the block that calls
tryAdoptSearchIndexCacheOverlay and then re-reads the overlay, wrap the awaited
adoption helper in a catch that ignores adoption failures so
readWikgArchiveMutationToken cannot abort resolution for otherwise readable
archives; keep the fallback behavior gated by SEARCH_INDEX_DATABASE_ENTRY_PATH
and the overlay file-kind check.

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.

1 participant