Design dual authoring modes and lazy projection fields
Document top-down and bottom-up writing, lazy title/abstractor/visual that persist after generation, and per-field regenerate plus lock. Wire into capability model and stage-1 notes.
This commit is contained in:
parent
64d4dcda7c
commit
e2aa4045f4
4 changed files with 253 additions and 2 deletions
|
|
@ -272,6 +272,7 @@ See **[spike-checklist.md](./spike-checklist.md)** — executable evaluation tas
|
|||
|
||||
- `docs/architecture/2026-08-12-content-substrate-exploration.md`
|
||||
- `docs/architecture/transclusion-and-chunks.md` — page-first model; chunks as extract/transclude
|
||||
- `docs/architecture/authoring-modes-and-lazy-projections.md` — top-down/bottom-up; lazy Title/Abstractor/Visual + lock
|
||||
- `docs/capability/stage-1-cutover.md`
|
||||
- `docs/decisions/2026-08-12-feature-cut-stage-1.md`
|
||||
- kontextual: `docs/architecture-blueprint.md`, `asset-registry-implementation.md`, `blob-storage-content-streaming-workplan.md`, `markitect-tool-reuse-boundary.md`
|
||||
|
|
|
|||
236
docs/architecture/authoring-modes-and-lazy-projections.md
Normal file
236
docs/architecture/authoring-modes-and-lazy-projections.md
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
# Authoring modes and lazy projection fields
|
||||
|
||||
| Field | Value |
|
||||
|-------|--------|
|
||||
| Date | 2026-08-13 |
|
||||
| Status | design proposal (product + data model) |
|
||||
| Related | page-first model, Abstractor/Title/Visual, stage-1 cut, markitect structure |
|
||||
|
||||
## Intent
|
||||
|
||||
Coulomb should make **both** styles of knowledge work feel natural:
|
||||
|
||||
| Mode | Flow |
|
||||
|------|------|
|
||||
| **Top-down** | Title → short description (abstractor) → outline → elaborate body → optional visual |
|
||||
| **Bottom-up** | Plunge into body text → title / abstractor / structure / visual emerge after or while writing |
|
||||
|
||||
Neither mode may force the other. Empty or content-only entities must still **display** as first-class cards (title, description, illustration) without blocking the writer.
|
||||
|
||||
## Principles
|
||||
|
||||
1. **Body is never blocked** on metadata. A page with only prose is valid.
|
||||
2. **Display needs projections** — card/list UI needs title, abstractor, visual when showing an entity.
|
||||
3. **Projections may start virtual** — generated on first need (lazy), then **persisted as normal field values**.
|
||||
4. **After persistence, fields behave like user-authored data** — editable, exportable in frontmatter, same SoR as hand-written values.
|
||||
5. **Regenerate** is an explicit action, not ambient rewriting.
|
||||
6. **Lock** freezes a field against automatic (and optionally against bulk) regeneration.
|
||||
7. **Structure stays page-first** — outline is headings/sections in the markdown body (or a derived outline view), not a separate chunk ontology. See `transclusion-and-chunks.md`.
|
||||
|
||||
---
|
||||
|
||||
## Field set (stage-1 aligned)
|
||||
|
||||
| Field | Role | Display use |
|
||||
|-------|------|-------------|
|
||||
| **title** | Name of the entity | Card headline, browser title, lists |
|
||||
| **abstractor** | Short description (Bubble: Abstractor) | Card subtitle, previews, search snippet |
|
||||
| **visual** | Illustration / cover | Card face, headers |
|
||||
| **body** | Main markdown content | Full page view |
|
||||
| **outline** | Derived (usually) from body headings | Top-down scaffold; navigation |
|
||||
|
||||
`outline` is typically **not** a separate SoR blob: it is **read from** `#` / `##` structure, or optionally written back as a stub section when the user accepts a generated outline in top-down mode.
|
||||
|
||||
---
|
||||
|
||||
## Dual mode UX
|
||||
|
||||
### Top-down path
|
||||
|
||||
```text
|
||||
New page
|
||||
→ focus title (optional visual placeholder)
|
||||
→ abstractor
|
||||
→ "Generate outline" → body becomes heading skeleton
|
||||
→ user elaborates under headings
|
||||
→ visual optional (upload or generate)
|
||||
```
|
||||
|
||||
- Empty body with title/abstractor is fine.
|
||||
- Outline generation **writes body** (or a dedicated outline region) only when the user asks — not on every keystroke.
|
||||
|
||||
### Bottom-up path
|
||||
|
||||
```text
|
||||
New page / open blank body
|
||||
→ write freely
|
||||
→ on first card/list display (or idle debounce): lazy-fill missing title/abstractor/visual
|
||||
→ user may edit, regenerate, or lock any field
|
||||
→ optional "Propose outline" restructures headings without deleting prose carelessly
|
||||
```
|
||||
|
||||
- Creating a page must not require a title dialog.
|
||||
- Autosave body continuously; projections fill **lazily on display need** or on explicit "Suggest metadata".
|
||||
|
||||
### Mode switching
|
||||
|
||||
There is **no mode toggle the user must set**. The product infers intent:
|
||||
|
||||
- If title/abstractor present and body empty → treat as top-down.
|
||||
- If body present and title empty → bottom-up; lazy projections allowed.
|
||||
- Both filled → normal edit; regenerate only on demand.
|
||||
|
||||
---
|
||||
|
||||
## Lazy projections (virtual → real)
|
||||
|
||||
### Lifecycle of a projection field
|
||||
|
||||
```text
|
||||
missing
|
||||
→ (display needs field) generate candidate [virtual / ephemeral]
|
||||
→ persist to page frontmatter [real]
|
||||
→ user edit | regenerate | lock
|
||||
```
|
||||
|
||||
| State | Meaning |
|
||||
|-------|---------|
|
||||
| **absent** | No value in SoR; may generate |
|
||||
| **derived** | Value present; `provenance: generated` (or equivalent); unlocked |
|
||||
| **user** | Value present; last material edit was human (or import); unlocked |
|
||||
| **locked** | Value fixed; no auto/lazy regenerate; explicit unlock required |
|
||||
|
||||
**Rule:** Once generated and saved, the value is **kept** as if the user had typed it — cards and export see real frontmatter, not a live prompt every time.
|
||||
|
||||
**Lazy trigger examples:**
|
||||
|
||||
- First render of a card in a list/deck when `title` or `abstractor` or `visual` is absent
|
||||
- Opening page header chrome when fields missing
|
||||
- Explicit “Fill missing metadata”
|
||||
|
||||
**Not triggers (by default):**
|
||||
|
||||
- Every keystroke in the body
|
||||
- Background jobs that rewrite locked or recently user-edited fields
|
||||
- Silent overwrites of `user` or `locked` values
|
||||
|
||||
### Generation sources (implementation-agnostic)
|
||||
|
||||
| Field | Cheap deterministic options | Richer options (optional) |
|
||||
|-------|----------------------------|---------------------------|
|
||||
| title | First H1, first non-empty line, filename/slug | LLM short title from body |
|
||||
| abstractor | First paragraph, first section under H1 | LLM 1–2 sentence summary |
|
||||
| visual | Palette placeholder from hash of id; first image in body/assets | Image model / pick from assets |
|
||||
| outline | Existing headings; or insert H2 stubs from summary | LLM outline from abstractor or body |
|
||||
|
||||
Prefer **deterministic first** for offline/dev and predictable tests; LLM behind the same regenerate/lock contract.
|
||||
|
||||
### Persist shape (illustrative frontmatter)
|
||||
|
||||
```yaml
|
||||
---
|
||||
id: page-…
|
||||
title: "Working title"
|
||||
abstractor: "One-line sense of the page."
|
||||
visual: "assets/visuals/page-….webp" # or sha256:… asset ref
|
||||
projections:
|
||||
title:
|
||||
state: derived # absent | derived | user | locked
|
||||
locked: false
|
||||
generated_at: "2026-08-13T12:00:00Z"
|
||||
generator: "first-h1@v1"
|
||||
source_hash: "sha256:…" # body hash when generated — optional staleness hint
|
||||
abstractor:
|
||||
state: user
|
||||
locked: false
|
||||
visual:
|
||||
state: locked
|
||||
locked: true
|
||||
generated_at: "2026-08-13T11:00:00Z"
|
||||
generator: "placeholder-hash@v1"
|
||||
---
|
||||
|
||||
Body markdown…
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- **Display values** live in top-level `title` / `abstractor` / `visual` so export and non-coulomb tools still see normal fields.
|
||||
- **`projections.*`** holds lifecycle only (state, lock, generator, hashes) — not a second title.
|
||||
- If `projections` is missing (imported Bubble/markdown), treat present fields as `user` unlocked; absent fields as `absent`.
|
||||
|
||||
### Actions on each field
|
||||
|
||||
| Action | Behavior |
|
||||
|--------|----------|
|
||||
| **Edit** | Set value; set state → `user`; clear stale generator metadata or keep last generator for audit |
|
||||
| **Regenerate / Update** | Allowed if not locked; re-run generator; overwrite value; state → `derived`; refresh `generated_at` / `source_hash` |
|
||||
| **Lock** | `locked: true`, state → `locked`; blocks regenerate and lazy auto-fill |
|
||||
| **Unlock** | `locked: false`; state → `user` or `derived` (keep last provenance) |
|
||||
|
||||
UI: per-field overflow (✎ edit · ↻ regenerate · 🔒 lock) on cards and page chrome — unobtrusive, always available.
|
||||
|
||||
### Staleness (optional, unlocked derived only)
|
||||
|
||||
If `source_hash` ≠ current body hash, UI may show “metadata may be outdated” and offer regenerate — **never** auto-regenerate locked or `user` fields without consent. Policy for `derived` + stale can be: badge only (default) or auto-refresh if product later opts in.
|
||||
|
||||
---
|
||||
|
||||
## Interaction with page-first / transclusion
|
||||
|
||||
- **Outline** = structure of **this** page’s body (headings), not a chunk table.
|
||||
- Top-down “generate outline” writes headings into body; elaboration is normal writing under those headings.
|
||||
- Bottom-up “propose outline” may reorganize into headings (explicit action; preview/diff recommended).
|
||||
- A card for a **transcluded excerpt** uses the **source page’s** projections unless the excerpt is promoted to its own page.
|
||||
|
||||
---
|
||||
|
||||
## Capability / feature mapping (rebuild)
|
||||
|
||||
Under `cap.space-content` (and any entity shown as a card):
|
||||
|
||||
| Feature id (proposed) | Description |
|
||||
|----------------------|-------------|
|
||||
| `feat.authoring-top-down` | Title/abstractor/outline-first create path |
|
||||
| `feat.authoring-bottom-up` | Body-first create; no required metadata gate |
|
||||
| `feat.projection-lazy-fill` | Lazy generate missing title/abstractor/visual on display |
|
||||
| `feat.projection-persist` | Persist generated values into frontmatter |
|
||||
| `feat.projection-regenerate` | Explicit per-field regenerate |
|
||||
| `feat.projection-lock` | Per-field lock against auto/regenerate |
|
||||
|
||||
Stage-1: ship dual authoring + lazy title/abstractor at minimum; visual generation can start as placeholder art; LLM optional.
|
||||
|
||||
---
|
||||
|
||||
## Anti-patterns
|
||||
|
||||
| Avoid | Why |
|
||||
|-------|-----|
|
||||
| Requiring title before first save | Blocks bottom-up |
|
||||
| Regenerating on every body keystroke | Noisy, fights the writer |
|
||||
| Virtual-only fields that never save | Breaks export and “user’s data” |
|
||||
| One global “AI mode” toggle for the whole page | Prefer per-field lock/regenerate |
|
||||
| Separate chunk entities only to hold outline | Outline is page structure |
|
||||
|
||||
---
|
||||
|
||||
## Open product choices
|
||||
|
||||
1. **Default generator set** — deterministic only vs LLM when configured.
|
||||
2. **Stale derived fields** — badge-only vs optional auto-refresh.
|
||||
3. **Visual generation** — placeholder vs asset pipeline vs external image model.
|
||||
4. **Outline write-back** — always mutate body vs ephemeral outline panel until “Apply”.
|
||||
5. **Space-level projections** — same contract for space cards (title/abstractor/visual).
|
||||
|
||||
---
|
||||
|
||||
## One-liner
|
||||
|
||||
**Top-down and bottom-up are both first-class; display fields can be born lazy, then live as ordinary locked-or-editable metadata so tooling never blocks the writing.**
|
||||
|
||||
## Related
|
||||
|
||||
- `docs/architecture/transclusion-and-chunks.md`
|
||||
- `docs/architecture/ArchitectureBlueprint.md`
|
||||
- `docs/capability/stage-1-cutover.md`
|
||||
- `docs/capability/existing-bubble-capability-map.md` (Title, Abstractor, Visual)
|
||||
|
|
@ -99,11 +99,24 @@ capabilities:
|
|||
status: partial
|
||||
notes: "Inferred from H1/filename; frontmatter/UI todo"
|
||||
- id: feat.page-abstract
|
||||
title: Page Abstract
|
||||
title: Page Abstractor
|
||||
status: todo
|
||||
- id: feat.page-visual
|
||||
title: Page Visual
|
||||
status: todo
|
||||
- id: feat.authoring-top-down
|
||||
title: Top-down authoring (title → abstractor → outline → body)
|
||||
status: todo
|
||||
notes: "docs/architecture/authoring-modes-and-lazy-projections.md"
|
||||
- id: feat.authoring-bottom-up
|
||||
title: Bottom-up authoring (body first; metadata later)
|
||||
status: todo
|
||||
- id: feat.projection-lazy-fill
|
||||
title: Lazy-generate missing title/abstractor/visual on display
|
||||
status: todo
|
||||
- id: feat.projection-regenerate-lock
|
||||
title: Per-field regenerate and lock for projections
|
||||
status: todo
|
||||
- id: feat.content-fail-closed
|
||||
title: Fail closed on missing binding/fetch
|
||||
status: shipped
|
||||
|
|
|
|||
|
|
@ -124,4 +124,5 @@ capabilities but does not redefine the must set.
|
|||
3. **Page body editing** — stage 1: product forms for Title/Abstract/Visual + markdown body path; exact editor TBD.
|
||||
4. **Delete** — hard delete vs archive (`is_active`)?
|
||||
5. **Transfer cohort** — require public self-registration, or LLDAP bulk invite for stage 1?
|
||||
6. **Content substrate** — see `docs/architecture/2026-08-12-content-substrate-exploration.md` (markitect-tool + markdown SoR; evaluate shard-wiki thin slice vs folder/git store).
|
||||
6. **Content substrate** — see `docs/architecture/2026-08-12-content-substrate-exploration.md` (markitect-tool + markdown SoR; evaluate shard-wiki thin slice vs folder/git store).
|
||||
7. **Authoring** — top-down and bottom-up both first-class; lazy title/abstractor/visual with regenerate + lock — `docs/architecture/authoring-modes-and-lazy-projections.md`.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue