approval-log.mjs
sha256:8d46372e39d2d5a54fd93a8b1c27922fe0d9b22a72197345f1d2c71701cc4ce2
feat(auth): persistent login system + C7 session introspection
Human
minor
⚠ breaking
17 days ago
| 1 | /** |
| 2 | * Materialized approval logs: thin vault markdown under approvals/ (indexed like notes). |
| 3 | * Default prefix avoids lib/config DEFAULT_IGNORE of folder name "meta". |
| 4 | */ |
| 5 | |
| 6 | export const APPROVAL_LOG_PREFIX = 'approvals'; |
| 7 | |
| 8 | /** |
| 9 | * @param {string} path - vault-relative path |
| 10 | * @returns {boolean} |
| 11 | */ |
| 12 | export function isApprovalLogPath(path) { |
| 13 | if (path == null || typeof path !== 'string') return false; |
| 14 | const n = path.replace(/\\/g, '/'); |
| 15 | return n === APPROVAL_LOG_PREFIX || n.startsWith(`${APPROVAL_LOG_PREFIX}/`); |
| 16 | } |
| 17 | |
| 18 | /** |
| 19 | * @param {{ path?: string, frontmatter?: Record<string, unknown> } | null | undefined} note |
| 20 | * @returns {boolean} |
| 21 | */ |
| 22 | export function isApprovalLogNote(note) { |
| 23 | if (!note) return false; |
| 24 | if (isApprovalLogPath(note.path)) return true; |
| 25 | const k = note.frontmatter && note.frontmatter.kind; |
| 26 | return String(k) === 'approval_log'; |
| 27 | } |
| 28 | |
| 29 | /** |
| 30 | * @param {string} proposalId |
| 31 | * @param {string} [approvedAtIso] - ISO timestamp (date prefix from first 10 chars) |
| 32 | * @returns {string} vault-relative path |
| 33 | */ |
| 34 | export function approvalLogRelativePath(proposalId, approvedAtIso = new Date().toISOString()) { |
| 35 | const day = String(approvedAtIso).slice(0, 10) || '1970-01-01'; |
| 36 | const safeId = String(proposalId) |
| 37 | .replace(/[^a-zA-Z0-9-]/g, '_') |
| 38 | .slice(0, 80); |
| 39 | return `${APPROVAL_LOG_PREFIX}/${day}-${safeId}.md`; |
| 40 | } |
| 41 | |
| 42 | function trunc(s, max) { |
| 43 | if (s == null || typeof s !== 'string') return ''; |
| 44 | const t = s.trim(); |
| 45 | return t.length <= max ? t : t.slice(0, max) + '…'; |
| 46 | } |
| 47 | |
| 48 | /** |
| 49 | * Payload for writeNote (string frontmatter values). |
| 50 | * @param {{ |
| 51 | * proposalId: string, |
| 52 | * targetPath: string, |
| 53 | * approvedAt: string, |
| 54 | * approvedBy?: string, |
| 55 | * proposedBy?: string, |
| 56 | * intent?: string, |
| 57 | * source?: string, |
| 58 | * proposedBodyExcerpt?: string, |
| 59 | * }} p |
| 60 | * @returns {{ relativePath: string, frontmatter: Record<string, string>, body: string }} |
| 61 | */ |
| 62 | const MAX_PROPOSAL_EXCERPT = 4000; |
| 63 | |
| 64 | export function buildApprovalLogWrite(p) { |
| 65 | const relativePath = approvalLogRelativePath(p.proposalId, p.approvedAt); |
| 66 | const targetPath = String(p.targetPath || '').trim() || 'unknown'; |
| 67 | const frontmatter = { |
| 68 | kind: 'approval_log', |
| 69 | proposal_id: String(p.proposalId), |
| 70 | target_path: targetPath, |
| 71 | approved_at: String(p.approvedAt), |
| 72 | }; |
| 73 | if (p.approvedBy) frontmatter.approved_by = String(p.approvedBy).trim().slice(0, 512); |
| 74 | if (p.proposedBy) frontmatter.proposed_by = String(p.proposedBy).trim().slice(0, 512); |
| 75 | const intentT = trunc(p.intent, 400); |
| 76 | if (intentT) frontmatter.intent = intentT; |
| 77 | const sourceT = trunc(p.source, 120); |
| 78 | if (sourceT) frontmatter.source = sourceT; |
| 79 | |
| 80 | const safeTitle = targetPath.replace(/`/g, "'"); |
| 81 | const bodyLines = [ |
| 82 | `Approved vault change applied to \`${safeTitle}\`.`, |
| 83 | '', |
| 84 | `- **Proposal ID:** ${p.proposalId}`, |
| 85 | `- **Approved at:** ${p.approvedAt}`, |
| 86 | ]; |
| 87 | if (intentT) bodyLines.push(`- **Intent (summary):** ${intentT}`); |
| 88 | |
| 89 | let body = bodyLines.join('\n'); |
| 90 | const ex = |
| 91 | p.proposedBodyExcerpt != null && String(p.proposedBodyExcerpt).trim() |
| 92 | ? String(p.proposedBodyExcerpt) |
| 93 | .replace(/\s+/g, ' ') |
| 94 | .trim() |
| 95 | .slice(0, MAX_PROPOSAL_EXCERPT) |
| 96 | : ''; |
| 97 | if (ex) { |
| 98 | body += '\n\n## Proposal excerpt (for search)\n\n' + ex + '\n'; |
| 99 | } |
| 100 | |
| 101 | return { |
| 102 | relativePath, |
| 103 | frontmatter, |
| 104 | body, |
| 105 | }; |
| 106 | } |
| 107 | |
| 108 | /** |
| 109 | * Filter search hits / results by content scope (path-only; no disk read). |
| 110 | * @param {{ path: string }[]} hits |
| 111 | * @param {'all'|'notes'|'approval_logs'} scope |
| 112 | * @returns {{ path: string }[]} |
| 113 | */ |
| 114 | export function filterHitsByContentScope(hits, scope) { |
| 115 | if (!hits || !scope || scope === 'all') return hits || []; |
| 116 | if (scope === 'approval_logs') return hits.filter((h) => h && isApprovalLogPath(h.path)); |
| 117 | if (scope === 'notes') return hits.filter((h) => h && !isApprovalLogPath(h.path)); |
| 118 | return hits; |
| 119 | } |
| 120 | |
| 121 | /** |
| 122 | * Map content_scope to vector-store folder prefix so ANN runs include path-restricted chunks. |
| 123 | * Post-filtering global top-k alone drops approval-log chunks when their similarity ranks below k. |
| 124 | * @param {'all'|'notes'|'approval_logs'|undefined} content_scope |
| 125 | * @param {string|undefined} userFolder - Hub folder filter (vault-relative prefix) |
| 126 | * @returns {{ folder?: string, wideNotesFetch: boolean, impossible: boolean }} |
| 127 | */ |
| 128 | export function resolveSearchFolderForContentScope(content_scope, userFolder) { |
| 129 | const cs = content_scope || 'all'; |
| 130 | const uf = |
| 131 | userFolder != null && String(userFolder).trim() |
| 132 | ? String(userFolder).trim().replace(/\\/g, '/').replace(/\/$/, '') |
| 133 | : ''; |
| 134 | if (cs === 'all') { |
| 135 | return { folder: uf || undefined, wideNotesFetch: false, impossible: false }; |
| 136 | } |
| 137 | if (cs === 'approval_logs') { |
| 138 | if (uf && uf !== APPROVAL_LOG_PREFIX && !uf.startsWith(`${APPROVAL_LOG_PREFIX}/`)) { |
| 139 | return { wideNotesFetch: false, impossible: true }; |
| 140 | } |
| 141 | return { folder: uf || APPROVAL_LOG_PREFIX, wideNotesFetch: false, impossible: false }; |
| 142 | } |
| 143 | if (cs === 'notes') { |
| 144 | return { folder: uf || undefined, wideNotesFetch: true, impossible: false }; |
| 145 | } |
| 146 | return { folder: uf || undefined, wideNotesFetch: false, impossible: false }; |
| 147 | } |
File History
2 commits
sha256:8d46372e39d2d5a54fd93a8b1c27922fe0d9b22a72197345f1d2c71701cc4ce2
feat(auth): persistent login system + C7 session introspection
Human
minor
⚠
17 days ago
sha256:6a102aafafdfe7e70a24f4e59740200f0ee713ce7915f1b53e9d4ba5ee8b4410
Initial Muse snapshot
Human
49 days ago