Skip to content

kb init: every routed owner page carries a Direct quick map - #267

Merged
tzhouam merged 1 commit into
mainfrom
kb/init-quick-maps
Sep 30, 2026
Merged

tzhouam merged 1 commit into
mainfrom
kb/init-quick-maps

Conversation

@tzhouam

@tzhouam tzhouam commented Sep 30, 2026

Copy link
Copy Markdown
Collaborator

Summary

Direct's product is the embedded map: for a repository routed by a knowledge-side _routes.yaml, every owner page must carry a ## … Direct … section (direct_routing._direct_quick_map: the first such heading up to the next ## , non-empty, ≤3500 chars), and test_every_routed_page_yields_a_quick_map fails CI otherwise. The first real run (afd-plugin skeleton, PR #265) routed pages that had none — one of its four CI failures. The manifest-routed half of that failure is the PR this one stacks on; this PR covers the knowledge-routed path (the empty-KB pilot, jiuwenswarm, would have hit it next).

What changes

  • kb_service/init_quick_maps.py renders a deterministic ## 代码快速入口(Direct) section on every owner page init writes or routes to: one row per active rule (its 触发 line or title · rule ID · the paths it names, else the owner's prefixes), or one 入口 row for a map card / entry page; ≤3000 chars (rows dropped from the end behind a | … | … | … | row), never over Direct's 3500. The heading opens with a word the format's loose ID scanner (ANY_RULE_HEADING) cannot take for an ID — ## Direct … reads as the ID Direct, and L1 refuses one ID on two pages. The section is first after the title, marked as init's own (<!-- kb-init:quick-map -->); a hand-written map (no marker) is never rewritten; content the page gains after the generated table (an index line, prose, another table) stays.
  • Stages: _Stage._conclude refreshes the maps of the owner pages the stage changed (deepen's appended rules become rows) and adds one to a routed page without one; a regenerated map that no longer fits is a blocking problem, never a stale map. validate_change then blocks — page named — when any owner page yields no map, or when a page this change wrote would be truncated as served (CI refuses new truncation), using the production extractor (_direct_quick_map now delegates to a text-taking _direct_quick_map_text).
  • Capacity counts the map: _apply measures each written rule page with its map as it will really be rendered (owner signals/prefixes from the routes file); an overflow reports page full and spills as before. A pre-existing owner page with no room for even its map is routed to the page that took its overflow (tracked by _place), else dropped with a checklist line. A briefing doc is never made an owner. Manifest-routed repositories write no map and are measured without one.

Tests

test/test_kb_init_quick_maps.py (22 tests): heading never a rule / never a duplicate ID; rows and triggers; entry rows; caps and escaping; regeneration keeps init's map only and everything the page gained after it (incl. a second table); a hand-written map is untouched; L1 sees a regenerated map as prose; blocking on a missing, unfitting, or newly truncated map; skeleton / modules / deepen chains pass a copy of the production check on every routed page; briefing docs are never owners; a full owner page is rerouted only to its overflow page; manifest-routed pages are measured without a map. The two skeleton capacity tests now count the map on the routed page. All kb init suites plus the four #265 CI tests pass; commit gate approved; local CI mirror green (SPEC freshness 93/93).

Stacked on the manifest-routed PR (kb/init-manifest-routed).

🤖 Generated with Claude Code

Direct's product is the embedded map: for a repository routed by a
knowledge-side _routes.yaml, every owner page must carry a "## … Direct …"
section (direct_routing._direct_quick_map: first such heading to the next
"## ", non-empty, at most 3500 chars) and
test_every_routed_page_yields_a_quick_map fails CI otherwise. The first
real run (afd-plugin skeleton, PR #265) routed pages that had none.

init_quick_maps renders that section deterministically on every owner
page init writes or routes to: one row per active rule on the page (its
触发 line or title, its ID, the paths it names, else the owner's prefixes)
or one 入口 row for a map card or entry page; at most 3000 chars, rows
dropped from the end behind a "…" row, never over Direct's 3500. The
heading is "## 代码快速入口(Direct)": the knowledge format's loose ID
scanner reads "## Direct …" as the ID "Direct", and L1 refuses one ID on
two pages, so the heading opens with a word it cannot take for an ID.
The section is the first after the title, marked as init's own, and L1
sees its changes as prose: rules on the page are untouched. A map init
did not write (no marker) is hand-written and never rewritten.

_Stage._conclude refreshes the maps of the owner pages the stage changed
(deepen's appended rules become rows; what the page gained after the
generated table — an index line, prose, another table — stays after it)
and adds one to a routed page that has none; a regenerated map that no
longer fits the page is a blocking problem, never a stale map. Then
validate_change blocks — with the page named — when any owner page yields
no map, or when a page this change wrote would be truncated as served
(CI refuses new truncation), using the same extractor the server runs
(_direct_quick_map now delegates to a text-taking
_direct_quick_map_text). A manifest-routed repository has no routes file
and is exempt.

Capacity counts the map: _apply measures each written rule page with its
map as it will really be rendered (the owner's signals and prefixes from
the routes file), so a page that would overflow reports "page full" and
the next rule spills to a sibling as before; a pre-existing owner page
with no room for even its map is routed to the rule page init wrote
beside it, or dropped with a checklist line; a briefing doc is never made
an owner. The two skeleton capacity tests now count the map on the
routed page.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 30, 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-30T18:15:32.340771Z 7d5d582 PR opened
ℹ️ 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.

@tzhouam
tzhouam changed the base branch from kb/init-manifest-routed to main September 30, 2026 18:12
@tzhouam
tzhouam merged commit 1d84a91 into main Sep 30, 2026
2 checks passed

@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: 7d5d5828d5

ℹ️ 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 on lines +190 to +193
for owner in (doc.get("owners") or []) if isinstance(doc, dict) else []:
if isinstance(owner, dict) and str(owner.get("path") or "") == page:
return ([str(x) for x in owner.get("signals") or []],
[str(x) for x in owner.get("scope_prefixes") or []])

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 Aggregate every owner mapped to the same page

When two valid _routes.yaml owners share a page, this returns after the first match, so a rule-less entry page gets a map containing only that owner's signals and prefixes. If Direct selects the second owner, it serves the same page with a quick map pointing at the first owner's code while read_required remains false; collect and deduplicate inputs from all owners whose path matches instead.

Useful? React with 👍 / 👎.

Comment on lines +260 to +263
elif status == "truncated" and (base is None or base.get(page) != text):
problems.append(f"quick map: {page}: the Direct section as served exceeds {HARD_CAP} characters "
"(truncated); keep the map first and short, and start the rules with a `## ` "
"heading so level-three rules are not part of it")

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 Reject truncated maps on newly routed pages

When a skeleton or modules change newly routes an existing page whose Direct section is already over the hard cap, the page text still equals base, so this condition suppresses the truncation problem even though the route itself is new. The stage can consequently publish a route that fails the every-routed-page quick-map check; truncation should be grandfathered only when the page was already routed in the base route table, not merely when its text is unchanged.

Useful? React with 👍 / 👎.

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