Use newline-only OKF aliases

This commit is contained in:
Franz Rolfsvaag 2026-06-18 23:01:27 +02:00
parent e63a75ae8a
commit 89326bb1f7
5 changed files with 25 additions and 5 deletions

View File

@ -33,6 +33,7 @@ Current state on `experimental-okf` as of 2026-06-18: standalone OKF plugin work
- Wired OKF into Lumi AI prompt context through the existing generic `registerContext` hook.
- Made Lumi AI context providers receive user/message/scope/origin context so per-user OKF permissions can be enforced.
- Ensured OKF AI context includes only published and approved entries and only fields visible to the requesting user.
- Changed OKF aliases/related questions to newline-only input so commas remain valid inside questions.
- Added `plugins/okf/tests/verify.js`.
### Remaining Work
@ -584,6 +585,7 @@ Current state on `experimental-feedback-system` as of 2026-06-18: the core feedb
## Done
- 2026-06-18: Updated OKF aliases/related questions to newline-only parsing and display so commas remain valid inside questions. No repo push yet.
- 2026-06-18: Continued `experimental-okf` locally: wired OKF into Lumi AI through the generic context provider hook, made AI context providers user/message-aware, and verified OKF AI context only includes published/approved entries and fields visible to the requester. No repo push yet.
- 2026-06-18: Continued `experimental-okf` locally: added shared core live user lookup, migrated OKF/Lumi AI/Moderation/Economy user-selection fields to it, fixed the moderation target search field, added OKF restore-from-version, and added OKF role-preview cards. No repo push yet.
- 2026-06-18: Started `experimental-okf` with a standalone OKF plugin: role-gated SQLite entries, sanitized Markdown browsing, admin/editor management, per-user OKF grants, workflow actions, version snapshots, and first verification coverage. No repo push yet.

View File

@ -284,7 +284,7 @@ function restoreVersion(db, entryId, versionNumber, actor, note = "") {
title: cleanText(snapshot.title, 180),
category: cleanText(snapshot.category, 120),
tags_json: JSON.stringify(splitList(snapshot.tags || [])),
aliases_json: JSON.stringify(splitList(snapshot.aliases || [])),
aliases_json: JSON.stringify(splitLines(snapshot.aliases || [])),
summary: cleanText(snapshot.summary, 800),
user_markdown: cleanText(snapshot.user_markdown, 24000),
moderator_markdown: cleanText(snapshot.moderator_markdown, 24000),
@ -401,7 +401,7 @@ function normalizeValues(values, { actor, existing, canImplement, now }) {
title: cleanText(values.title || existing?.title, 180),
category: cleanText(values.category ?? existing?.category, 120),
tags_json: JSON.stringify(splitList(values.tags ?? parseJsonArray(existing?.tags_json))),
aliases_json: JSON.stringify(splitList(values.aliases ?? parseJsonArray(existing?.aliases_json))),
aliases_json: JSON.stringify(splitLines(values.aliases ?? parseJsonArray(existing?.aliases_json))),
summary: cleanText(values.summary ?? existing?.summary, 800),
user_markdown: cleanText(values.user_markdown ?? existing?.user_markdown, 24000),
moderator_markdown: cleanText(values.moderator_markdown ?? existing?.moderator_markdown, 24000),
@ -489,6 +489,15 @@ function splitList(value) {
.slice(0, 50);
}
function splitLines(value) {
if (Array.isArray(value)) return value.map((item) => cleanText(item, 240)).filter(Boolean).slice(0, 50);
return String(value || "")
.split(/\n+/)
.map((item) => cleanText(item, 240))
.filter(Boolean)
.slice(0, 50);
}
function cleanLinkList(value) {
return splitList(value)
.filter((item) => /^(https?:\/\/|\/(?!\/)|#)/i.test(item))

View File

@ -48,7 +48,7 @@ const entry = createEntry(db, {
slug: "currency-basics",
category: "Community",
tags: "currency, coins",
aliases: "How do coins work?\nWhat is currency?",
aliases: "How do coins, bonuses, and payouts work?\nWhat is currency?",
summary: "Explains the community currency.",
user_markdown: "Users earn **coins** through activity.",
moderator_markdown: "Moderators may help users with missing transaction context.",
@ -61,6 +61,7 @@ const entry = createEntry(db, {
}, admin);
assert.equal(entry.slug, "currency-basics");
assert.deepEqual(entry.aliases, ["How do coins, bonuses, and payouts work?", "What is currency?"]);
assert.equal(listEntries(db, {}, user).length, 0);
assert.equal(listEntries(db, {}, mod).length, 1);
assert.equal(getEntryBySlug(db, "currency-basics", user), null);

View File

@ -137,7 +137,8 @@
</div>
<div class="field">
<label>Aliases / related questions</label>
<textarea name="aliases" rows="3" placeholder="One per line or comma-separated"><%= selected ? selected.aliases.join('\n') : '' %></textarea>
<textarea name="aliases" rows="3" placeholder="One related question per line"><%= selected ? selected.aliases.join('\n') : '' %></textarea>
<span class="hint">Use one question per line. Commas are kept as part of the question.</span>
</div>
<div class="field full">
<label>Short summary</label>

View File

@ -19,7 +19,14 @@
</p>
<% } %>
<% if (entry.aliases && entry.aliases.length) { %>
<p class="hint">Related questions: <%= entry.aliases.join(", ") %></p>
<div class="hint">
<strong>Related questions</strong>
<ul>
<% entry.aliases.forEach((alias) => { %>
<li><%= alias %></li>
<% }) %>
</ul>
</div>
<% } %>
</section>