tickets
All repositories: gitoria
5.5 KB
// lib/mdview.hl — THE MARKDOWN READ VIEW FOR LLM READERS (ticket #6, mission 014; CONCEPT.md "Data// formats": a Markdown read view for LLM readers). The ticket GET endpoints answer// `Accept: text/markdown` with a compact Markdown document instead of JSON (lib/api-helpers.hl// `wantsMarkdown`, `markdownReply`); JSON stays the default and is unchanged. Statics only.//// one ticket → # <project> #<n>: <subject>// state · opened (by) · updated · formerly · URL// the summary as written (it IS Markdown)// ## History — one `### <seq> · <when> · <author> <what>` per event, its text below// a list → # Tickets (<filters>): <count>// one line per ticket: `- <project> #<n> [<state>] <subject> · updated <when> · <url>`// Texts are embedded as written (they are Markdown already), so a text's own headings sit// inside the document — readers should take the `### <seq> ·` lines as the event boundaries.import { publicUrl } from './users.hl'static NL = ""// one line: line breaks and runs of spaces become one spacestatic oneLine = (s) => {if (s == null) { return '' }parts = []for (w of ('' + s).replaceAll(NL, ' ').split(' ')) { if (w != '') { parts.push(w) } }return parts.join(' ')}// `> ` in front of every line (a previous summary inside an edit event)static quoted = (s) => {out = []for (l of ('' + s).split(NL)) { out.push(l == '' ? '>' : '> ' + l) }return out.join(NL)}static ticketLine = (row) => {return '- ' + row.project + ' ' + row.ref + ' [' + row.state + '] ' + oneLine(row.subject) + ' · updated ' + row.updated + ' · ' + publicUrl + row.href + relationTail(row)}// ---- relations (ticket #4, mission 017) --------------------------------------------------------// one ticket's document gets "Parent:", "Children (k of n done):", "Blocked by:", "Blocks:"// blocks under its URL; a list line gets ` · parent … · children n, k done · blocked by … · blocks …`static refName = (r) => { return r.project + ' ' + r.ref }static refLine = (r) => { return '- ' + refName(r) + ' [' + r.state + '] ' + oneLine(r.subject) + ' · ' + publicUrl + r.href }static confirmedOf = (refs) => {let n = 0for (r of refs) { if (r.state == 'done') { n = n + 1 } }return n}static relationTail = (row) => {parts = []if (row.parent != null) { parts.push('parent ' + refName(row.parent)) }total = row.children.lengthif (total > 0) { parts.push('children ' + total + (row.allChildrenConfirmed ? ', all done' : ', ' + confirmedOf(row.children) + ' done')) }if (row.blockedBy.length > 0) {bs = []for (r of row.blockedBy) { bs.push(refName(r) + ' [' + r.state + ']') }parts.push('blocked by ' + bs.join(', '))}if (row.blocks.length > 0) {bs = []for (r of row.blocks) { bs.push(refName(r)) }parts.push('blocks ' + bs.join(', '))}return parts.length == 0 ? '' : ' · ' + parts.join(' · ')}static relationLines = (t) => {lines = []if (t.parent != null) {lines.push('')lines.push('Parent: ' + refName(t.parent) + ' [' + t.parent.state + '] ' + oneLine(t.parent.subject) + ' · ' + publicUrl + t.parent.href)}total = t.children.lengthif (total > 0) {lines.push('')lines.push('Children (' + (t.allChildrenConfirmed ? 'all ' + total + ' done' : confirmedOf(t.children) + ' of ' + total + ' done') + '):')for (r of t.children) { lines.push(refLine(r)) }}if (t.blockedBy.length > 0) {lines.push('')lines.push('Blocked by:')for (r of t.blockedBy) { lines.push(refLine(r)) }}if (t.blocks.length > 0) {lines.push('')lines.push('Blocks:')for (r of t.blocks) { lines.push(refLine(r)) }}return lines}// a list: { tickets } (rows, as the JSON answer) + a label of its filters ('' = all)static listDocument = (rows, filters) => {lines = []lines.push('# Tickets' + (filters != '' ? ' (' + filters + ')' : '') + ': ' + rows.length)lines.push('')for (r of rows) { lines.push(ticketLine(r)) }return lines.join(NL) + NL}static eventHeadline = (e) => {let what = e.labelif (e.kind == 'state') { what = 'changed the state: ' + (e.from != '' ? e.from : '?') + ' → ' + e.to }if (e.kind == 'edit') {parts = []if (e.subjectChanged) { parts.push('subject') }if (e.summaryChanged) { parts.push('summary') }what = 'edited the ' + parts.join(' and ')}return '### ' + e.seq + ' · ' + e.when + ' · ' + oneLine(e.author) + ' ' + what}// one ticket: { ticket (row), events (rows, oldest first) } as the JSON answerstatic ticketDocument = (data) => {t = data.ticketlet opener = ''for (e of data.events) { if (e.kind == 'created' && opener == '') { opener = oneLine(e.author) } }lines = []lines.push('# ' + t.project + ' ' + t.ref + ': ' + oneLine(t.subject))lines.push('')let meta = 'state: ' + t.state + ' · opened ' + t.created + (opener != '' ? ' by ' + opener : '') + ' · updated ' + t.updatedif (t.hasOld) { meta = meta + ' · formerly ' + t.oldRef }lines.push(meta)lines.push(publicUrl + t.href)for (l of relationLines(t)) { lines.push(l) }lines.push('')lines.push(t.summary != '' ? t.summary : '(no summary)')lines.push('')lines.push('## History')for (e of data.events) {lines.push('')lines.push(eventHeadline(e))if (e.kind == 'edit') {if (e.subjectChanged) {lines.push('')lines.push('Subject before: ' + oneLine(e.oldSubject))}if (e.summaryChanged) {lines.push('')lines.push('Summary before:')lines.push('')lines.push(e.oldSummary != '' ? quoted(e.oldSummary) : '> (empty)')}}if (e.hasText) {lines.push('')lines.push(e.text)}}return lines.join(NL) + NL}
Branches
- mainmain branch
Latest commits
- a75e0279mission 010 (code order) 3/4: let only where a variable is reassigned or re-bound in a loop body (456 lets → plain declarations; Hybriel refuses a plain declaration inside a loop on its 2nd pass). gate 249/0, connect 60/0, real-data reads identical, a 50-step write sequence (API + faces) identical to the old codemre
- e9d5c618mission 010 (code order) 2/4: one lib/ file per topic — store.hl split into projects / tickets (+ relations) / events / tickets-helpers, util.hl shared helpers (env, storage dir, URLs, sorts, Vienna time), the function routes out of project.hl into lib/api.hl (thin; auth/filters/Accept in api-helpers.hl), invite + member-removal logic out of the faces/routes into invites.hl / tickets.hl; project.hl is the map. /login/callback gets req + the session store by reference. gate 249/0, connect 60/0, real-data reads identicalmre
- 97e269b5mission 010 (code order) 1/4: .hl files out of the root — lib/ (store, users, connections, invites, migrate, markdown, mdview, import = ticketfile, util = localtime, jsoncheck, api-helpers = api), tools/import.hl, components/styles.hl; import paths only. gate 249/0, connect 60/0, real-data reads identicalmre
- 38f9d10ftickets: Hybriel master 06617221 (plugin allocators 3a781359 + 413f60e4, mpackdb 2cb7ae5e, http1 773de63e); gate 249/0, connect 60/0mre
- d3db6139tickets: Hybriel master 190aa11d (fc838894 GC correctness, #126 closure scopes, #127); gate 249/0, connect 60/0mre
- bce182e3tickets: Hybriel master 7eea0d32 (#126 memory, #48 lambda copies its argument); migrate.hl lambdas take &logmre
- 4137be0fantcolony#40: mission references point to the moved missionsmre
- 9bfba36aantcolony#40: history (LOG.md), worker briefs (missions/) and reports moved here from antcolony, numbered per project; old numbers in antcolony docs/mission-map.mdmre
- c7bd2645tickets: Hybriel master 73267707 (#122 fixed); compactNow workaround removed (#110 covered)mre
- 2ab91ee9tickets: gate checks rows appear once (session sync); re-vendor to ff51cf46 stopped on hybriel#122, stays 837fe120mre
- e01c2b1dtickets#24: installable app (manifest, service worker, offline list), own icon; gate waits for the hello's pongmre
- 752fbb7fdeploy.sh: back up live storage/.sessions/.env before every deploy (newest 5 kept)mre
- 38bdd5e4deploy.sh: never send .git or .gitignore to Byrodinmre
- f12fa1bcState of 2026-09-27, before the move to gitoriamre