Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions docs/PROGRESS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ file at the **end of every step** so we never jump ahead on a deferred item or
skip a main phase.

- **Last updated:** 2026-07-23 - see [PROGRESS §7 Changelog](#7-changelog). This field carries the date only; the narrative lives in §7.
- **Active branch:** `docs/close-45-and-log-design-items` - this docs-only PR CLOSES register **#45** (the live duplicate Vercel ingestion service is gone and `INGESTION_TOKEN` was rotated) by converting its row in place (🔴→✅, original lead preserved after "Was:"), and logs four new product-design items as registers **#46-#49** (settings-page premium polish, per-material actions, landing-page nav menu, animated terminal demo). Touches **only** this file (the #45 row, four new register rows, one new §7 block, and these three header fields). **No code, no other register row's substance, no §1-§6 phase content, no B5b row, `PIVOT_PLAN.md` untouched.** Register **#17** stays OPEN.
- **Main tip:** `ecb1ed5` (PR **#67** merge - the duplicate-ingestion-service removal: the `experimentalServices.ingestion` sub-key dropped from `vercel.json`, keeping `frontend`), on top of `2d0856d` (PR **#66** - the §7 changelog migration off the line-8 header field, all 17 entry bodies byte-identical) and `c119f6b` (PR **#65** - register #17 WIP preservation snapshot). Both required checks green on `ecb1ed5` (`tsc`, `db-types`).
- **Active branch:** `docs/b5b-scoping-record` - this docs-only PR RECORDS the **B5b upload-content-hardening scoping** as register **#50** and creates the decision record [`b5b-scoping.md`](./b5b-scoping.md): the established+verified constraints (the real ceiling is Vercel's hard ~4.5 MB edge cap, not the app's broken 50 MB promise; duration is the unmeasured true limit), the corrections (the cap does not stop ZIP bombs; `PIVOT_PLAN.md:269`'s 50 MB claim is false), the B5b-1/B5b-2 split plus enablers (b1)/(b2), and **seven OPEN founder decisions** with an OUTSTANDING duration measurement. Touches **only** two files: `docs/b5b-scoping.md` (new) and this file (one new register row #50, one new §7 block, and these three header fields). **No code, no route/validation/size-constant change, no other register row's substance, no §1-§6 phase content; B5b §1 row stays ⬜ (scoping is not progress), `PIVOT_PLAN.md` untouched.** Register **#17** stays OPEN.
- **Main tip:** `1e37c03` (PR **#68** merge - the docs-only close of register #45 and logging of design items #46-#49), on top of `ecb1ed5` (PR **#67** - the duplicate Vercel ingestion service removed from `vercel.json` + `INGESTION_TOKEN` rotated) and `2d0856d` (PR **#66** - the §7 changelog migration off the line-8 header field, all 17 entry bodies byte-identical). Both required checks green on `1e37c03` (`tsc`, `db-types`).

> ✅ **RESOLVED 2026-07-04 — Supabase project back online (register #21).** The
> project **auto-paused** (free-tier inactivity) and then hit Supabase's free
Expand Down Expand Up @@ -146,6 +146,7 @@ project memory, and per-step review debates. "Where" is the target phase/trigger
| 47 🔎 **OPEN — product-design item, raised 2026-07-23 (Abo Jad design review). DESIGN-QUALITY, NOT A BUG.** | **Materials page has no per-item management** — a material, once listed, cannot be acted on. | Not a defect — a missing-affordance/UX item; no phase assigned. | Wanted: **per-material actions** — **rename**, **delete**, and similar. |
| 48 🔎 **OPEN — product-design item, raised 2026-07-23 (Abo Jad design review). DESIGN-QUALITY, NOT A BUG.** | **Pre-login landing page has no navigation menu.** | Not a defect — a missing-nav/UX item for the mobile app; no phase assigned. | Wanted: a **hamburger (three-line) icon** opposite the KnowFlow wordmark in the header, opening a **main dropdown menu** for the mobile app. |
| 49 🔎 **OPEN — product-design item, raised 2026-07-23 (Abo Jad design review). Carries a correctness rider, not pure polish.** | **The landing page's terminal-style demo window shows static text.** **Cross-ref #20:** that window currently displays **"Ready in 0.4s"**, a timing claim **#20 already flags**, and this session proved **ingestion is synchronous and takes real seconds** — so the number MUST be corrected or removed as part of this work rather than re-shipping a false claim. | Not pure polish — the embedded "0.4s" IS a false claim (see **#20**), so this row carries a correctness rider; no phase assigned. | Wanted: **animate the terminal** so the text **types out live from start to finish**; as part of the same work, **correct or remove "Ready in 0.4s"** (per **#20**) rather than re-ship it. |
| 50 🔎 **SCOPING RECORDED 2026-07-23 — B5b upload content hardening. Decisions 1-7 OPEN pending founder rulings; the duration measurement OUTSTANDING. Scoping is NOT progress — B5b (§1) stays ⬜.** | **B5b (deep upload content hardening: magic-byte, decompression-bomb / nested-archive limits, content scanning) is now scoped in [`b5b-scoping.md`](./b5b-scoping.md).** Established + verified this session: the real upload ceiling is **Vercel's hard ~4.5 MB edge cap** (platform constant, unraisable), **not** the 50 MB the app promises in **7 places** (`route.ts:51/:52`, `DropZone.tsx:26`, four i18n strings — the `:52` message is hardcoded English, register-#17 class); ingestion runs on **Railway** (`INGESTION_SERVICE_URL`), so the `route.ts`→`/convert` hop faces no second Vercel cap but a **9-13× return-path blob** (each chunk's 1024-float embedding inline + full markdown, materialized by `await pyResponse.json()`); and **no `maxDuration` is set anywhere** (grep-empty), so the true user-facing ceiling is an **UNMEASURED duration limit**, not size. Corrections recorded so they are not re-inherited: the 4.5 MB cap does **NOT** mitigate decompression bombs (compressed bytes are bounded, expansion is not); the pre-auth body-buffering concern is **bounded** by the edge cap, so the `getUser`-above-`formData` reorder is a low-priority ride-along (`checkDocumentLimit` **cannot** move — needs `kbId` from the body; `enforceLimit` **should not** — it would burn an upload credit on a rejected request); and **`PIVOT_PLAN.md:269`'s "Size cap (50 MB) already exists" is FALSE** (the effective cap is the platform's ~4.5 MB). | Not a bug and not deferred work — a **decision record** written before implementation so the founder's rulings are captured, not inferred. The split is recorded (B5b-1 request-path magic-byte + declared-size ZIP-ratio pre-check + the auth reorder, doable now; B5b-2 enforced expansion bounds + nested depth + AV/scanning, genuinely gated on **B6**; plus enabler moves (b1) persist chunks from Railway and (b2) signed-URL direct-to-storage). **No code, no route/validation/size-constant change; `PIVOT_PLAN.md` untouched.** | **Seven OPEN founder decisions** (each stated with consequences in the doc, none picked here): (1) max file size — accept ~4.5 MB vs. deliver real 50 MB via (b2); (2) format list — keep all six vs. drop the docx/pptx/xlsx ZIP trio that carries all bomb risk; (3) scanning scope — AV (nothing in the image today), PII, and **prompt-injection in extracted text** (flows unfiltered into Haiku via `<document>` interpolation — currently unmitigated, needs at minimum a documented accepted-risk decision); (4) failure posture — reject-at-upload (415) vs. accept-then-quarantine; (5) enforcement point — Next, Railway, or both (Railway trusts its caller on a single bearer token, so Next-only is bypassed if `INGESTION_TOKEN` leaks); (6) the three ZIP numbers (expansion ratio, absolute expanded cap, nesting depth) — each measured from the spoofable declared central directory (cheap) or actual expansion (accurate, needs B6); (7) the signature table (`%PDF-`; `PK\x03\x04` + inner part-path for the OOXML trio; the txt/md non-signature UTF-8/NUL rule). **OUTSTANDING measurement:** at what file size does the upload time out — plain-`.txt`-on-Preview procedure + failure-signature table recorded in the doc §5 (each upload burns a rate-limit credit and the #22 free tier — throwaway subject, delete rows after). Related: **#45** (the duplicate ingestion service closed while scoping this), **#22** (bounds (b2) and the §5 test budget), **#24** (why `enforceLimit` cannot move), **#17** (the `:52` hardcoded-English class). |

**Founder-owned open items (PIVOT_PLAN §10) — not engineering-blocked, tracked for launch:**
Next.js hosting decision (Vercel Pro vs self-host, ~Phase 7/10) · **Supabase backend
Expand Down Expand Up @@ -207,6 +208,10 @@ bodies total 37,095 bytes. **This retires the Option C frozen-tail invariant by
existed only to police a boundary inside an unreviewable single line, and the append-only rule above
supersedes it. No bespoke hash is needed for future updates: the diff is the proof.

### 2026-07-23 - B5b upload-hardening scoping recorded (register #50, docs/b5b-scoping.md)

(**B5b (deep upload content hardening) is now SCOPED, not started — register #50 opened, `docs/b5b-scoping.md` created; §1 B5b stays ⬜ because scoping is not progress on the gate.** The doc records this session's established + verified facts, its corrections, the split, and seven OPEN founder decisions. **Established (each verified against the code this session):** the real upload ceiling is Vercel's **hard ~4.5 MB edge cap** (platform constant, unraisable — `413 FUNCTION_PAYLOAD_TOO_LARGE` before the handler), **not** the 50 MB the app promises in **7 places** (`route.ts:51` check / `:52` hardcoded-English 413 message — register-#17 class, `DropZone.tsx:26`, and four i18n strings in `en.ts`/`ar.ts`); ingestion runs on **Railway** (`INGESTION_SERVICE_URL = knowflow-production.up.railway.app`), so the `route.ts`→`/convert` hop faces no second Vercel cap but a **9-13× return-path blob** — `/convert` returns each chunk's **1024-float embedding inline plus full markdown** (`main.py`: `EMBED_DIM=1024`, `CHUNK_TOKENS=512`, `CHUNK_OVERLAP=64`), and `await pyResponse.json()` materializes it in the Next function's memory; and **no `maxDuration` is configured anywhere** (grep-empty across `vercel.json`/`next.config.ts`/`package.json`/`src`), with Voyage embeds batched 128-at-60s and Supabase inserts batched 50, all sequential in one invocation — so the true user-facing ceiling is an **UNMEASURED duration limit**, not size. **Corrections recorded so they are not re-inherited:** the 4.5 MB cap does **NOT** mitigate decompression bombs (it bounds compressed bytes, never the expansion — 4.5 MB of crafted ZIP still exhausts the service); the pre-auth body-buffering concern is **bounded** by the edge cap, making the `getUser`-above-`formData` reorder a low-priority ride-along (`checkDocumentLimit` **cannot** move — it needs `kbId` from the body; `enforceLimit` **should not** — it would burn an upload credit on a rejected request, register #24); and **`PIVOT_PLAN.md:269`'s "Size cap (50 MB) already exists" is a documented FALSE claim** (the effective cap is the platform's ~4.5 MB), corrected here, `PIVOT_PLAN.md` deliberately untouched. **The split:** B5b-1 (request-path, doable now — magic-byte verification, a declared-size ZIP-ratio pre-check from the central directory, the auth reorder); B5b-2 (gated on **B6** — enforced expansion bounds, nested-archive depth, AV/content scanning, all needing actual expansion or a worker); plus enabler moves **(b1)** move chunk+embedding persistence into the Railway service (kills the 9-13× blob, relocates slow work to the only component without a duration ceiling — the enabler of B6, not B6 itself) and **(b2)** signed-URL direct-to-Supabase-storage upload (the only way past the 4.5 MB inbound cap; gated on the file-size ruling; #22's 5 GB/mo egress limits its payoff to ~100 uploads/mo at 50 MB). **Seven OPEN decisions (stated with consequences, none picked): (1)** max file size — ~4.5 MB vs. real 50 MB via (b2), gating the enforcement point and sync/async posture; **(2)** format list — keep six vs. drop the docx/pptx/xlsx ZIP trio that carries all bomb risk; **(3)** scanning scope — AV (nothing in the image today), PII, and **prompt-injection in extracted text** (flows unfiltered into Haiku via `<document>` interpolation across summarize/quiz/agent — unmitigated, needs at minimum a documented accepted-risk decision); **(4)** failure posture — reject-at-upload (415) vs. accept-then-quarantine; **(5)** enforcement point — Next, Railway, or both (Railway trusts its caller on a single bearer token, so Next-only is bypassed if `INGESTION_TOKEN` leaks); **(6)** the three ZIP numbers (expansion ratio, absolute expanded cap, nesting depth) — each measured from the spoofable declared central directory vs. actual expansion; **(7)** the signature table (`%PDF-`; `PK\x03\x04` + inner part-path for the OOXML trio; the txt/md non-signature UTF-8/NUL rule — txt/md have no magic bytes at all). **OUTSTANDING measurement:** the duration ceiling — plain-`.txt`-on-Preview procedure + a failure-signature table (`200`+chunk_count=pass; `413`+Vercel HTML=edge cap; `413`+app JSON=app check; `500`+"Ingestion failed"; `504`/none=duration), each upload burning a rate-limit credit and the #22 free tier. Related registers: **#45** (the duplicate ingestion service closed while scoping this), **#22**, **#24**, **#17**. **Docs-only: no code, no migration, no DB, no route/validation/size-constant change; only `docs/b5b-scoping.md` (new) and this file changed; `Phase 7` stays ⬜, §1 B5b row untouched, `PIVOT_PLAN.md` untouched, register #17 stays OPEN.**)

### 2026-07-23 - closed register #45 and logged four product-design items (#46-#49)

(**Register #45 CLOSED and four new design registers opened.** #45 — the live DUPLICATE Vercel ingestion service at `/_/ingestion` — is now closed: the `experimentalServices.ingestion` sub-key removal shipped in PR #67 (merge `ecb1ed5`), and the closure evidence was MEASURED, not asserted — before merge `curl https://tryknowflow.com/_/ingestion/health` returned **200 with no redirect**; after merge the same curl returned **307 → `/ar/_/ingestion/health`** and, following it, **404** (Next's own page, not the service JSON), proving nothing intercepts the path at the edge any more and that **config removal alone sufficed** — no Vercel-dashboard service deletion was needed. `INGESTION_TOKEN` was then **rotated Railway-first then Vercel** and verified by a **real production upload reaching READY**. The #45 row was CONVERTED IN PLACE (🔴→✅, original lead preserved after "Was:"), per the #38/#40 keep-history convention. Separately, Abo Jad's design review raised **four product-design items**, logged as registers **#46-#49**, all 🔎 OPEN — overarching framing: the app reads as visually **empty/cold** and does not match a **paid premium product**; these are DESIGN-QUALITY items, not bugs. **#46** the settings page needs premium polish (language-switcher buttons, a dark-mode toggle, further refinement); **#47** the materials page needs per-item actions (rename, delete, and similar); **#48** the pre-login landing page needs a hamburger nav menu opposite the wordmark for the mobile app; **#49** the landing page's terminal demo should type out live from start to finish, AND its **"Ready in 0.4s"** claim — already flagged by **#20** and disproven this session (ingestion is synchronous, takes real seconds) — must be corrected or removed rather than re-shipped. **Docs-only: no code, no migration, no DB; `Phase 7` stays ⬜, §1 B5b row untouched, `PIVOT_PLAN.md` untouched, register #17 stays OPEN.**)
Expand Down
Loading
Loading