Skip to content

feat(web): reader customization of wiki pages (human prior injection) - #447

Draft
YashJayswal24 wants to merge 2 commits into
mainfrom
yj/wiki-customization
Draft

feat(web): reader customization of wiki pages (human prior injection)#447
YashJayswal24 wants to merge 2 commits into
mainfrom
yj/wiki-customization

Conversation

@YashJayswal24

Copy link
Copy Markdown
Collaborator

Summary

Turns the inert "Refresh this wiki" button into a Customize control: a reader
states how they want a page written — a free-text instruction and/or a section
structure — and the page is regenerated to match. This is the "human prior
injection" space discussed for the demo (Zhongming: "leave space for human prior
injection"
): the grounded wiki stays authoritative, and the reader gets to layer
their own intuition on top.

Key design choice: this is a presentation layer, not a change to the grounded
generation pipeline
(AgentWiki, _PAGE_PROMPT_VERSION = "102", plan → page →
repair → quality guards). The page already exposes rendered markdown; the
Customizer rewrites that markdown in one LLM pass, instructed to preserve
every citation marker and add no facts. It never touches the plan/quality-guarded
generator, so it can't destabilize grounding — and it's fail-soft: any error
returns the default markdown unchanged.

Changes

  • codenib/wiki/customizer.py (new) — Customizer.apply(markdown, instruction, structure) -> markdown. One LLM pass reusing the wiki model/creds; preserves
    citations, strips a stray ```markdown fence, returns the input
    unchanged on empty prior or any error.
  • codenib/web/customization_store.py (new) — global, in-RAM prior store.
    Two scopes (wiki, page) cascade most-specific-wins; resolve(page_id)
    merges them. Memoizes transformed markdown per (source, prior). Lazy cleanup
    on access: idle-TTL (30 min) + LRU cap (64) — no background thread, so the
    single-process deploy needs no scheduler. Narrow interface so a shared backend
    (only needed for multi-worker serving) is a one-file swap.
  • codenib/web/app.pyPOST /api/repos/{id}/customize (set/return),
    DELETE …/customize (clear), and GET …/wiki/{page_id} applies any active
    prior and sets a customized flag. Whole-wiki priors apply lazily on view.
    A single Customizer + CustomizationStore are created in lifespan.
  • codenib/web/schemas.pyCustomizeRequest / CustomizeResponse.
  • FrontendCustomizePanel.tsx (scope selector, instruction textarea,
    optional structure field, Apply, Reset), replacing the refresh button; gated
    off in the static export (customizationAvailable()), which has no backend.
    WikiPage.customized + customizeWiki / resetCustomization in lib/api.ts.

Design doc: .claude/design/wiki-customization-design.md.

Behavior

  • Ephemeral & global. Priors live in RAM, never on disk. The durable default
    wiki is authoritative; customizations vanish on restart (and after idle-TTL).
  • Cascade. A whole-wiki "house style" plus per-page overrides; the page
    instruction appends after the wiki one, a page structure replaces the wiki one.
  • Honesty. A customized page is labeled a reader lens — the transform is
    told to preserve citations and add no facts, but it is not re-run through
    the grounding guards, so it is never presented as re-grounded.

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update
  • Refactoring
  • Performance improvement
  • Tests

Testing

  • Tests pass locally
  • Added new tests for the changes

pytest test/web/186 passed. New test/web/test_customization.py covers
the store (cascade / override / memoize / TTL / LRU / drop / unknown-scope), the
customizer (transform / no-op-without-LLM-call / fail-soft / fence-strip /
citation-preserving prompt / bounds), and the endpoints (page round-trip, reset
restores default, empty clears, whole-wiki applies lazily on view).
test/web/test_app_runtime.py updated for the new customized flag.
tsc --noEmit clean; black, isort, flake8, and the CodeNib namespace check pass.

Out of scope (follow-ups): section-level scope + selection UI; per-visitor
isolation (session token — the store key already has room); a shared/Redis store
for multi-worker serving.

Checklist

  • My code follows the project's style guidelines
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • My changes generate no new warnings
  • Any dependent changes have been merged and published

YashJayswal24 added 2 commits August 6, 2026 06:30
Turns the inert "Refresh this wiki" button into a Customize control: a reader
states how they want a page written — a free-text instruction and/or a section
structure — and the page is regenerated to match. This is the "human prior
injection" space discussed for the demo.

It is a presentation layer, not a change to the grounded generation pipeline.
The page already exposes rendered markdown; Customizer rewrites that markdown in
one LLM pass, instructed to preserve every citation marker and add no facts. It
never touches the plan/quality-guarded generator, and is fail-soft: any error
returns the default markdown unchanged. A customized page is labeled a reader
lens, never presented as re-grounded.

Priors are ephemeral and global, held in RAM (CustomizationStore), never
persisted: the durable wiki stays authoritative and customizations vanish on
restart. Two scopes cascade most-specific-wins — a whole-wiki house style plus
per-page overrides. Cleanup is lazy on access (idle TTL + LRU cap), so the
single-process demo needs no scheduler. The store sits behind a narrow
interface so a shared backend (only needed for multi-worker serving) is a
one-file swap.

API: POST /api/repos/{id}/customize sets a prior and, for page scope, returns
the transformed markdown; DELETE clears it; GET wiki/{page_id} applies any
active prior and flags `customized`. Whole-wiki priors apply lazily on view.
Frontend: a Customize panel (scope, instruction, optional structure, Apply,
Reset), gated off in the static export where there is no backend.

Tests: store cascade/override/memoize/TTL/LRU/drop; customizer transform,
no-op, fail-soft, fence-strip, citation-preserving prompt; endpoint round-trip,
reset, clear, lazy whole-wiki. 186 web tests pass; tsc clean; pre-commit clean.
@github-actions github-actions Bot added the type/test Test-related label Aug 6, 2026
@fishmingyu
fishmingyu marked this pull request as draft August 7, 2026 19:47
@fishmingyu

Copy link
Copy Markdown
Member

Returning this feature to Draft pending an explicit isolation/security decision. The current POST/DELETE customization API is unauthenticated and writes a process-global store, so any reader can change or clear the lens seen by every other reader. The transformed page also is not re-run through grounding guards. Require at least per-session/visitor isolation or administrator authorization, cross-user/concurrency regressions, and a deliberate grounding policy before this can re-enter the merge queue.

@fishmingyu fishmingyu added type/feature New functionality status/needs-info Waiting for reporter to provide more info effort/large 1 week+ security Security-sensitive issue or fix and removed type/test Test-related labels Aug 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/large 1 week+ security Security-sensitive issue or fix status/needs-info Waiting for reporter to provide more info type/feature New functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants