Repository navigation
Apex v0.1.9e — Build 15
Pre-releaseBuild 15
Build 15
- Every case gets its chat, and Navi files her own findings (issue #161). Opening a case now ensures a chat tied to that case — created on first open, returned to ever after, one canonical thread per case, riding the server-side chat store (#139) like every other thread and marked with a CASE tag on the Chats tab. Open the case chat on the case page walks straight into it. While a case is open, Navi can file her own findings into it: conclusions she reaches, in her own words, under her own name — the finding's frontmatter carries
author: navi, the case page marks it FILED BY NAVI, and a line lands in the case's timeline. Filing is never silent and never background: it happens in the thread where she reached the conclusion, and the receipt — the finding's title and its path — hangs on her message there (the same judgment-marker discipline as her memory saves, at most two per reply; a stray marker with no case open is stripped and dropped). The open case is visible: the status column carries it in its stats, and the case chat wears a chip naming it that jumps back to the case; closing the case clears both. - Case exports show image evidence as images (issue #162). The report's evidence section now embeds PNG/JPG/GIF/WebP evidence into
report.mdas base64 data URIs — the evidence travels inside the single file, so the #138 single-file promise holds for the downloaded copy too, not just in the app's own preview. An image over ~8 MB stays listed by name with the reason stated; non-image evidence keeps the name-and-size listing; the internal-withholding rules are untouched. - A case can be deleted — completely (issue #163). The case page gains a delete control with one confirmation that names what will be removed: the case, every file it owns in the Vault (findings, evidence, timeline, manifest, exported report), and — since this build ties chats to cases — its tied chat, the whole conversation. Deleting purges the case folder from the Vault entirely, removes the bound chat from the store (so no machine's write-through can resurrect it), and clears the open-case pointer when the deleted case was the open one. The confirmation states the backup truth plainly: backups are snapshots and are not reached into — a case inside an existing backup stays in that backup.
- The Threat Intelligence Center, v1 (issue #165). A new Apps surface and a new engine (
intel.py, stdlib only) beside the bridge: a local intel store (config/intel.db) fed by keyless public sources only — the NVD API 2.0 (keyless tier, incremental lastMod windows, politely paced), the CISA KEV JSON catalog, EPSS (daily, batched over the store), CISA advisories RSS, and curated RSS (Krebs, BleepingComputer). A record's identity is its CVE ID, and each source owns its fields — KEV the exploited status and due dates, NVD the CVSS/CPE/description, EPSS the likelihood — merged by UPSERT, fetched with ETag/Last-Modified discipline, and stamped per source so staleness is labeled everywhere (fresh <12h · aging <48h · stale beyond; last-good is served, never a silent old picture). Navi is contextually aware: the digest enters her chat context as a bounded standing brief (~700 tokens, freshness stamps included), a deliberate lookup (intel: <query>, “what do we know about CVE-…”) answers straight from the store without a model round-trip, and naming a CVE or a watched product in ordinary chat pulls that record into the reply (in a case chat, the case's matched records ride too). Matching is local and declared: the operator's watchlist, the Mesh roster, and words from open cases — CPE-grade matches labeled exact, text hits labeled keyword; the inventory never leaves the machine. The Mesh carries the weight: one node collects, the digest object (navi-intel-digest/1, ~300 ranked records, collector and per-source stamps in its header) travels the Mesh door to thin clients that import it and match locally; a receiver with the fallback off never polls the feeds, and the fallback is a setting, never a surprise. From a record, File into the open case lands it as a finding with its citation (CVE ID + digest stamp) written into the note, so case exports carry it. The page is desktop-first — freshness strip, triage queue, the record, and the brief rail — with a dedicated phone arrangement (one column, brief first) in the same round.
Build 14
Build 14
- The Guardian — a system monitor that protects Navi's headroom (issue #156). A new engine (
guardian.py, stdlib only) lives in the system-integration layer beside the bridge, awake independent of the UI: it samples processes from/proc, keeps a headroom model of what Navi needs (a memory reserve that tightens while Navi is generating), and learns per-process collision statistics in a small persistent store (config/guardian-stats.json) — how often a process has collided, warned, been throttled, or been killed. Its action ladder is watch → warn → throttle → kill: throttle is a reversible duty-cycle (SIGSTOP/SIGCONT slices to a CPU cap) plus a best-effort renice, and kill (SIGTERM, grace, SIGKILL) may only ever touch processes on the operator-approved expendable list, which ships empty on purpose. Its own processes, the bridge, and the llamafile are protected by PID, name, and uid. Every action lands in a bounded in-memory log and an append-onlyconfig/guardian-log.jsonl; every event carries provenance from day one —origin(which device and door it came from) andforeground(what the operator is doing there,unknownuntil a later pass teaches it to see) — so the future mobile-whisper and foreground-posture concepts need no schema migration. The face is a new Utilities → Guardian page: a summary card with a plain-words pressure chip and a Mesh line (borrowing/peers when the Mesh is present, working locally only when it is not — the Guardian is mesh-aware and degrades gracefully), a live watch table, the learned-offenders table, the full ladder settings (persisted in config, restored on restart), and the action log. On a phone (≤800px) the page is a dedicated interface, not a squeezed desktop: cards stack full-width, the PID column drops, and every row, input, and the Save button meets the 44px floor. Navi can answer questions about the Guardian in chat: the bridge appends the Guardian's current state as a short structured block (≤110 words) to the chat system prompt, andGET /api/guardian/state/POST /api/guardian/settingsexpose the same state to the page. The Guardian's own footprint was measured in this build's audit (issue #157): about 0.20% of one core for its sampler loop and no measurable resident memory beyond the module import. - The optimization pass (issue #157). Measured first, on the bench, against the Build 13 tree as baseline with an identical stub harness: bridge spawn → status 412.4 ms, chat round-trip median 1,261.9 ms, of which the bridge pipeline itself is only ~6.2 ms — there was no fat worth cutting, and no cosmetic optimization is claimed. What the numbers did justify shipped: an automatic hardware profile — a config with no
hardware_profilenow profiles the machine once on startup (detect + derive), persists it, and fills derived runtime defaults only where the operator has not set them, so modest machines get sane context, threads, and batch without a setup step; the profile view now names its tier plainly (potato / standard / beast) and where the profile came from (profileSource). Warm-up removes the model-load dead air before the first ask: after a bridge-initiated llamafile start reports healthy, one one-token warm-up completion fires (gated byruntime.warmup— explicit setting wins; unset, it is on for potato/standard tiers and off for beast), andruntime.warm_at("HH:MM") schedules a daily warm through the same start path. The derivation table itself is unchanged — its 6–12 GiB row's ctx 4096 is the size already proven fluid on the 9.4 GiB Framework baseline, and doubling it would eat the Guardian's reserve on exactly the machines the Guardian protects. The full before/after table is posted on issue #157. - Arm at boot, from inside the app (issue #159). The status column's Servers → Boot row — which has read the armed state live since Build 12 — gains the hand to go with the eyes, and Settings → System carries the same control: Arm at boot registers and enables both systemd user services (
navi-llamafile.service,navi-bridge.service) from the unit templates the package ships, with this install folder substituted, exactly as the installer's boot choice does, and records the startup choice in config. Linger is attempted the one way the app honestly can — a plainloginctl enable-linger, no sudo, nothing borrowed from the parked sudo toggle — and when the system refuses it, the surface shows the exactsudo loginctl enable-linger $USERcommand instead of pretending, and the arm shows as half-armed (services enabled, starting at login rather than boot). After every action the state is re-read from the system and shown as found; partial success shows as partial. Disarm disables both units — what is running keeps running, it simply will not come up at boot — and leaves linger as it is. Both directions are idempotent, starting an already-running service is adoption, never doubling, and arming is a local action only: the bridge refuses it through the DHD door. - The rail takes its settled order (issue #160). The left-rail groups now seat as Apex | Navi, Apex | Apps, Apex | Systems, with Apex | Utilities low by the version line — and the group seated low opens upward, its children stacked above its header, as it has since Build 6; the settled spec now says so on purpose. Order and direction only: every group's contents, children, icons, and behavior are exactly as they were, and the order is proven in pixels by the real-browser geometry gate on desktop and phone, not just in the markup.
- Headless auto-start gets its full guide, in both docs surfaces. The in-app Docs gain a Headless auto-start page — setup, the Wi-Fi secret fix, and the boot test — covering the installer boot choice (there is no Settings toggle for it; armed state is read live in the status column's Servers → Boot row), the netplan/NetworkManager trap where a Wi-Fi profile with no stored secret fails at boot with "no secrets: No agents were available for this request" until a desktop login provides an agent (fix: store the secret with
nmcli, typed at the machine, never through chat), the no-login verdict test (reboot, leave the login screen untouched, reach the bridge from a phone on the same Wi-Fi), and failure diagnosis from the journal. The same guide ships to the GitHub wiki as "Headless Auto-Start and the No-Login Boot Test".
Build 13
Build 13
- The Chip Studio fits a phone, and focus replaces fullscreen (issue #146). On a phone (≤800px) the Studio stops being a desktop page squeezed into a column: it works one pane at a time — Face, Code, Test — under a full-width segmented switch; the face art becomes a collapsible strip instead of a competing pane; and Run / Forge / Save sit in a bottom action bar pinned to the thumb, every target ≥44px, the bar reserving its height in the layout so nothing ever slides under it (the #133 line — the editor frame reserves its true height, head, strip, switch and bar taking their own rows and the panes scrolling in exactly what remains). A snippet shelf and an autocomplete row — completing from the chip API's own vocabulary plus Python's keywords — are the primary input aids, and no desktop feature is cut. Run tests the forged chip itself, through the bridge's own run route, and lands its output in the Test pane. Above 800px nothing about the Studio's layout changes — except the one deliberate retirement: the editor's old fixed-position fullscreen is gone, and the Studio takes the Vault's focus mode (#135) as the app's one expand idiom — rail, status column, presence bar and the Studio's own header rows yield, and Esc or the toolbar button brings the room back.
- Case Files wears the Investigator (issue #149). The generic folder glyph gives way to the folder with a magnifier over its lower-right corner, drawn in the same stroke hand as the rest of the set — everywhere the Case Files nav item renders: expanded rail, collapsed rail, and phone.
- The badge gets a door, and the sync stops spinning (issue #150, with the #136 field fixes). A badge minted on one machine used to have no way into another — Build 12 read a badge from exactly two places, and the picture file in the operator's hand was neither. Settings → Operator now carries a true badge slot beside the minting controls: pick the badge picture and its
navi-badgepayload is parsed — kind must be "operator-badge", anything else is refused in plain words and the held badge is untouched — and the GitHub Account sync card carries the same slot: one shared intake feeding the one badge state (CONFIG.operatorBadge), no second store. The Operator section names the badge currently slotted and this node's name beside it. The sync card's labels tell the truth per state — the no-badge button no longer reads as a bare pull; it says no badge is slotted — and the bootstrap path is written on the card: mint here, back up, slot elsewhere, or pull a backup that carries one. Pressing the sync button now goes to Chat at press time, where the operation card carries the progress and the verdict, and the start runs under a 45-second timeout that fails in plain words in both rooms instead of leaving Settings spinning. The sync machinery itself — fetch, preview, confirm, apply — is untouched. - The DHD door wears the Row's icon (issue #151). Apex serves the Nocturne Row logo as its favicon: favicon-scale derivatives of the site's logo.png — a 180×180 apple-touch icon and a 32×32 tab icon, about 20 KB between them, never the 287 KB original — bundled as app assets, linked from the page with relative paths, and served by the bridge on both doors: at the bridge origin, and through the DHD proxy behind the same passcode gate as every other dashboard byte. The installer lands them like the other binary assets, in plain words when a git clone has none to land.
- Amendments, found by this build's own gates and fixed in the same bake: the Case Files rail icon had rendered as an empty frame since the view shipped in Build 12 — nav children resolved their icon by label, and "Case Files" is the one label that is not its view key; children now resolve by key, which is what lets the Investigator above actually appear. On the phone the Studio's switch, snippet and action buttons now state their own 44–48px floors (the shared phone floor had quietly set them to 40px), and the Studio root takes the bounded frame the other work views use — the Forge button, its confirmation card and the documents tail ride inside the panes' scroll region, so the frame reserves its true height with the action bar at its foot.
Build 12
Build 12
- Chats live on the server now (issue #139). A chat used to live only in the browser window that held it — localStorage, per install, per browser origin — so the laptop's threads and the server's threads were different conversations, and picking up where you left off depended on which door you came in. The store of record is the bridge now: plain files in
config/chats/, one JSON per thread, written atomically, with the uploads index beside them. Every window hydrates from the server on load and writes through on every change; localStorage is demoted to a cache and an offline fallback, and the first browser to meet an empty store imports its existing chats as the seed — migrated, never discarded. Open Apex on the desktop, through DHD, or from a second browser and it is the same conversation. Chats also become the ninth backup essential: they ride the ready-to-fly backup on their own tick, and a restore converges them rather than overwriting — newest wins per thread when one copy is the other's continuation, and where both machines genuinely moved the same thread on, both survive, the incoming copy arriving as a dated sibling thread, never a silent merge. - Account sync: slot the Operator Badge, pull the latest backup (issue #136). The Badge (#116) was always a pointer, never a credential — now it has its errand. Slotting it in Settings → GitHub & Backup (its home, with the account machinery) asks the bridge to pull the latest GitHub backup and build the same per-item preview Import builds — presented as a live operation card in Chat, Navi speaking before it and after it, nothing generated while it runs, and nothing applied until you confirm on the card. The GitHub sign-in remains the only key: signed out, the sync stops at its first step with a polite direction and no fetch is attempted. Her node keypair and Mesh certificate never ride a backup, so they can never ride a sync.
- Headless server mode, and boot that tells the truth (issue #142).
start.sh --headlessstarts the services only — the llamafile, check-first as always, and the supervised bridge — and opens no window: SSH-safe, idempotent, with the same start.log the desktop launch writes. A desktop launch adopts services that are already running instead of starting second copies. The installer's boot mode registers a second user service,navi-bridge.service, beside the llamafile's, and tries to enable lingering; where it cannot, it prints the exactloginctl enable-lingercommand rather than letting you believe boot-start is armed when it is not. The status column's Servers group gains a Boot row that reads the armed state — both services, the linger state — from the system every time it shows, never from memory. - Case Files, the first Apex | App — and the Vault takes its seat beside it (issue #137). A new nav group, Apex | Apps, opens with Case Files and the Vault directly underneath (placement only — the Vault's view and its #134/#135 frame are untouched). A case is a folder of plain markdown in the Vault —
cases/<name>/, its manifest incase.md's frontmatter (client, scope, status, opened) — so cases ride the Vault's backups and open in Obsidian as they lie. The view cold-starts on the case list; a case carries Findings as structured cards rendered from the finding notes, Evidence as files dropped straight into the case folder, and a Timeline newest first. Mark a case open and Navi files to it from chat — "file that as a finding" lands the exchange in the open case, out loud — and the latest chat or chip output files as a finding from the case page. - The Case Files report export (issue #138). One markdown document per case: the head (name, client, scope, dates, status), the findings by severity in full, the timeline as an appendix, the evidence listed — landed as
report.mdin the case folder with a matching download. Findings marked internal are withheld and counted in the document ("2 internal notes withheld"), never silently omitted, and no internal machine names or Mesh details are ever written into a report. The export is markdown only: the app has no PDF path today, and the report does not pretend otherwise. - A first chip for the Navi Federation (issue #144, preview).
navi-mud-literides this build as a Ready, unpublished preview chip — not in the package, not published: a local-only starter world ("The First Hearth", seven rooms) seeded as plain markdown room files, walked in chat (look,go <exit>,take <item>), with a machine-readablefederation.jsonbeside it (worldName,naviFederation: true,rooms,links) — the contract under which a world can later federate over Mesh without rework. No networking, no listeners, no code execution from world files: the worlds are data. - Amendments, same build (maintainer's note): the bake's own gates caught one defect in the new convergence code before anything shipped — a repeated sync of the same backup would mint a fresh sibling thread for a still-diverged chat every time; convergence now recognizes a diverged copy it has already filed and keeps it, so the same sync lands the same way twice. Second, the geometry gate caught the rail at 150% UI scale: with a fourth group open, the version footer ran past the rail's foot and the rail clipped it (overflow hidden) — the rail now scrolls vertically when its content exceeds its height, the way the phone's menu panel already did, so every row and the footer stay reachable at any scale. Also corrected wherever this build found them: the standing claims that chat transcripts are never backed up (Settings → GitHub & Backup and the in-app Docs) — as of this build, chats are an essential of their own, and the copy says so.
Build 11
Build 11
- The Vault's tree moves to the left, and the desk reaches the bottom of the window (issue #134). The files panel sat on the right of the editor by explicit grid placement — the .80 build's answer to a flex row that rendered differently in the shell and in browsers — and the whole view ended where its content did, the editor a fixed sixteen rows with dead space beneath it on any tall window. The placement is reversed at the operator's direction: the tree leads on the left, the way the app's own rail leads its views, and the Vault is a height-bound frame now, the way Docs is — the title, intro, search, and recent rows hold still as fixed heads, the pair flexes into every pixel of height that remains, the tree scrolls its own length inside its panel, the editor stretches to the bottom of the window in Edit, Split, and Preview alike, and the backlinks strip rides the foot of the editor's column. On a phone the same frame stacks — the tree in a capped band with its own scroll, the editor flexing below it — and nothing is left trapped in a sliver.
- The editor can take the whole window (issue #135). A focus button in the editor's header — corners out to enter, corners in to leave, and Escape on the keyboard as the other door — puts everything else away: the menu rail, the status column, her presence bar, and the Vault's own head rows, and the tree, until the note is the only thing in the window — still inside Apex, never the browser's fullscreen. Leaving focus, by the button, by Escape, or by switching views, restores the Vault exactly as it was — same note, same mode, same unsaved words — because focus touches nothing but the frame: the state lives in one flag and the frame's face is read from it, the pane button's discipline. On a phone the top bar yields as well, and the exit stays a full-size touch target.
- Amendments, same build (maintainer's note, filed with issue #134's evidence): two bake finds, both caught by this build's own gates before anything shipped. First, the editor card sat at the pair's head at content height, the textarea one line tall — the pair's single stretching row had gone to the tree alone, the editor auto-placed into an implicit second row; both columns are pinned to the declared row now. Second, the mode buttons were dead in the shipped page — their wiring's selector also matched the editor column's own mode attribute (added this build to key the preview's flex share to Edit), and the column, wired last, overwrote the mode with its own value on every click; the wiring is scoped to the buttons and the attribute renamed. The gate now clicks the real buttons, the way the operator does.
- On a phone, her presence lives in one place (issue #140). The presence bar rode above every view on a phone just as on a desktop — and the status pane's head row carried the same line, painted by the same hand from the same source, so opening the dock showed her twice. The phone bar is retired: her portrait and name now head the status pane itself, beside the line that already lived there, and the pane button goes with the bar — the top bar's dock toggle is the phone's one door, where the desktop keeps both of its doors and nothing about them changes. The height the bar and its margin took comes back to the content: the chat thread, and every other view with it, gains the measure by the frame's own flex geometry, not by a new sum anyone has to keep true.
- The composer footer fits its frame, and clearing a chat is a command (issue #133). The hint row under the chat bar was sliced at its bottom edge at rest — on a desktop the mode pill's lower border went, on a phone the wrapped second row was cut at the screen's edge, empty thread or full: the Chat frame ends one padding above the window's foot, and the composer's bottom bleed, which exists to meet that foot, was clipped at the frame's edge instead. The frame now reaches the window's bottom edge itself, the composer gives its true height inside it, and the whole footer paints — the same boxes in the same places, the clipped band simply shown. And the row's widest member is gone: the Clear chat button is retired in favour of the composer's own command — type
/clsand send it, in either mode, and the thread empties with its fold state reset, the line never sent, never run, never in the history — so the footer reads the same in an empty thread as in a full one. - The status pane's stale warnings tell the truth again (issue #141). The page's own build stamps froze at Build 4 — the bridge's stamps advanced every build after, the page's never did — so on every healthy install since, the status column compared a Build 10 bridge against a page calling itself Build 4 and cried wolf twice: a red BRIDGE OUT OF DATE over a bridge that was current, and an amber PREVIOUS BUILD banner naming the wrong build, which no reload could clear because the reloaded page carried the same frozen stamps. The Updater read the same frozen number for the build it was running. The stamps are unified again — page and bridge stamped together, as the first builds were — and the packaging now refuses a mismatched pair outright, so the class cannot ship twice.
Build 10
Build 10
- The bottom of the chat stays at the bottom of the screen (issue #131). On a phone, the Chat frame's height was reckoned as the viewport minus the top bar alone — the presence bar above the thread, the page's own padding, and the frame's foot were never in the sum — so the whole frame stood taller than the screen by exactly their height, the composer's foot sat that far below the screen's edge, and the page's scroll was what carried it in and out of view: scrolling could drag the composer along with the page. The frame is height-bound on phones now: while Chat is up, the app frame takes the viewport's height exactly, the centre column flexes inside it under the presence bar, and the chat view flexes inside that — so the composer's place at the foot is the frame's own geometry, not a number anyone has to keep true, and nothing that rides above it can push it off the edge. The message list is the only thing in Chat that scrolls; the composer does not move, and its width is untouched.
Build 9
Build 9
- The pane button's face tells the truth (issue #130). The presence bar's pane button could sit painted in the accent — the open face — over a closed dock, its own pressed state reading closed the whole time: the button is rebuilt by every render, and a button born under the operator's finger (the tap that opened the dock, on a phone) or a resting cursor picked the accent up from the generic ghost hover rule and kept it after the dock was dismissed another way. Its face is keyed to the frame itself now: the accent follows the same #app state classes that show and hide the column and the dock — the classes that are the visibility — its pressed state is read off those classes rather than re-derived from a parallel flag, its hover feedback is a background tint that can never wear the state accent, and its color no longer transitions, so the face cannot lag the fact either. One state, read from the one place it is rendered: the button cannot disagree with what is on screen.
Build 8
Build 8
- Her status bar is part of the frame now — and its button is a button again (issues #123, #115, #114 revisited at the operator's ruling). Navi's presence — her portrait, her dot, her name, her living line — leaves the Chat view and becomes frame chrome: one persistent bar above every view, Chat, Settings, Docs, all of them, repainted from the same single presence source and never rebuilt, so it cannot flicker or reset as you move around the app. And the status column's pane button is restored to the bar's top right in its original form — the panel glyph with a pressed state — on every page, not just in Chat: #123 had retired it in favor of the menu chrome's text toggle, which reads as a menu option, not a toggle. And the menu chrome's text toggle — the door #123 left in its place — is retired outright at the same ruling: exactly two doors remain, the bar's pane button on every page and the phone's top-bar dock button, both wearing the panel glyph. Every door drives the one column state, their states in agreement, and the #114 cold-start rules are unchanged — the column still starts hidden on desktop, its state unremembered, the menu groups folded.
- The portrait actually arrives (issue #128). Build 7 shipped Navi's portrait inside the package, but the installer never copied it into the install — it copies its assets by name, and the portrait's name was never on the list — so the bridge's portrait route found no file and chat showed no picture: no gutter faces, no presence-header portrait, on every Build 7 install. Build 8's installer carries the portrait into the install exactly the way it carries the app icon, on fresh installs and Updater applies alike (the Updater runs this same installer). And when a copy of the sources has no portrait to copy — a git clone, which carries none of the binary asset files — the installer now says so in plain words and carries on: chat shows without her picture, and nothing else about the install changes.
Build 7
Build 7
- Chat gets faces (issue #116). Every conversational message now carries its author: a picture in the gutter, the author's name, and the time it was sent — older messages show the date too. Navi's picture is her own portrait — Navi leaning against the lamp post, her established key art — bundled with the app, and the same face sits in the #115 presence header at the top of the Chat window. Your picture is uploadable in the Operator tab (trimmed to a square, kept with your profile on this machine, and carried in the settings backup); until you upload one, a picture generated from your name — a seeded spray field with your initial — stands in. The call transcript in Settings → Voice reads the same way, faces, names, and times included, its · SPOKEN marker intact; turns are stamped when they happen, on both sides.
- Operator Badges — your self-portrait as a cartridge (issue #116). The Operator tab mints a badge PNG in the card-grade face format — your chosen face in the window: your uploaded photo, or the generated art — with a small payload inside the picture in the same data-in-picture convention as chips and Mesh cards: your display name, and this node's name. The badge is viewable and exportable from the Operator tab, and your chat picture can be the badge itself. The trust line is the spec's own: a badge is a self-portrait, not a credential — its payload is self-asserted, nothing reads a badge to prove identity, Mesh pairing is untouched, and a badge whose payload is damaged simply reads as no badge.
- One status toggle, reachable from anywhere (issue #123). The status column had three doors; it has one per shape now. The Chat header's toggle is gone. On desktop the « Status control in the menu chrome is the door, present on every view; on a phone the top bar's button is the door, wearing the professional panel glyph that used to live in the chat header. Toggling from any view raises the column — or the phone's dock — in place, on the Status tab; you are never carried off to Chat to reach it. The #114 cold-start rules stand: the column starts hidden on desktop, the state is not remembered, and the menu groups start folded.
- Two tabs, honestly named (issue #124). Settings' second tab is Persona now, not "Operator & Persona" — the Operator tab is the operator's, the Persona tab is hers, and nothing moved: Soul and Memory editors, the save-mode leash, the proposal queue, and her file all stay exactly where #110 put them. The order reads Operator · Persona · Appearance · Voice · GitHub & Backup · System, and a settings tab value saved by an older build lands on a valid tab instead of a blank one.
- The phone top bar must stay put (issue #125). On phones the top bar is now fixed to the viewport instead of sticky — the page scrolls beneath it, and the content keeps its exact 60px offset, applied once. The mechanism is proven on the bench; the issue itself closes on a phone in the field, the way it was filed — iOS is the judge of its own scrolling.
- Node leaves the nav rail (issue #126). Apex | Systems reads Mesh · DHD — Node's standalone rail entry is retired, because Node lives where it has lived: as its own section on the DHD page, and as its own row in the status column's Servers list, in the standing order Navi · Apex Node · Apex Mesh · Apex DHD, unchanged. DHD is still one tap from the rail.
Build 6
Build 6
Build 6 carries the Build 4 menu restructure and the Build 5 field-round fixes — builds that reached field test but were folded forward before standalone publication — together with this round's own work:
- The menu's groups cannot collide anymore, and the dividers travel (issues #105, #106). The Systems children's fold was built as a grid whose template named one row while every child button took a row of its own — the extra rows were sized by nothing and painted over whatever followed, which is why an expanded group ran over its neighbors on a phone. The children now live in a single inner track, the shape the Docs topics always used, and the group's break line is the group's own last element: it sits directly beneath the group's last visible row, folded or open, mid-glide and settled. Apex | Utilities gains a fourth member: Updater leaves the flat block and joins the utilities group, which reads top to bottom Settings · Updater · GitHub · Docs on screen, desktop and phone alike.
- The status column is a tabbed pane now (issues #107, #108). A tab bar sits at the very top of the column — Status first, Chats second, with the chat count — and exactly one pane shows beneath it. Status is the default, and what a phone's dock opens on. The Status pane is the column as it has always been; the Chats pane is the chat manager whole — switch, new, delete, uploaded files — moved up beside the tabs instead of living at the column's foot. When something wants your eye — an update waiting, a stale bridge or page — the Status tab carries a small amber dot while you are in Chats.
- One motion language, and a Potato mode for weak machines (issue #109). Every transition in the app — menus, panes, views, cards, presses, the Docs topics' glide — now runs on shared motion tokens, one set of durations and one curve, instead of a duration typed wherever a transition was wanted; the operating system's reduced-motion setting collapses the whole vocabulary to instant. Settings → Appearance gains Potato mode: "For low-power machines — turns off decorative animation across Apex." On, state changes are cuts, indicators hold still as static state, and the Dark Liquid Glass theme's backdrop blur is suspended too. The choice persists in the config beside the theme and rides the settings backup with it.
- Settings grew an Operator tab, and the Memories tab retired (issue #110). The tab order is now Operator · Operator & Persona · Appearance · Voice · GitHub & Backup · System. Operator holds what Navi calls you, and your file — About you — memories for Navi: the entries that exist because you put them there, added by hand or saved from chat at your ask. Operator & Persona keeps the Soul and Memory document editors and gains her side: the save-mode leash, the proposal queue, and Her file — what she keeps — the entries that exist by her judgment. It is one list underneath, split by the origin every entry already carries; the Build 4 pointer card is gone — the document editor is on the very tab the leash lives on now, with nothing left to point at.
- The menu widened, the glyphs got honest, and the interface learned to scale (issue #111). The expanded menu is 196px now — at 170 the group headers' chevrons clipped at the rail's edge. The Systems children wear their own icons: Node has a server glyph of its own instead of borrowing DHD's dial (the icon keys off the child now, not the view it opens), and Apex | Mesh wears the hex trio — three hexagons in a cluster, one above and two below — replacing the two-hexagon mark that read as a horizontal Venn diagram. And Settings → Appearance gains a UI scale (100% · 110% · 125% · 150%): the whole interface, words and layout together, like an operating system's display scale — one knob on purpose, because a separate text size would multiply against it and squeeze the fixed rows at the big end.
- A third group joins the menu: Apex | Navi (issue #112). Between Systems and the flat block, Apex | Navi gathers her mind and tools — Chips, ChipDeck, RAG — and the Vault stays flat below it: the shared desk, belonging to neither family. The status column also loses its Call button in this round: the VOICE stat reports state, and starting a call lives where it always also lived — the composer's Call button and the call panel in Settings → Voice.
- The Voice view is retired; Settings → Voice is the one voice home (issue #113). The call panel moved whole into the Voice settings tab — the big Call button and the in-call panel with its wave, clock, and transcript; Hear her voice; the pipeline strip with each stage's real state; her settings card; the engines-not-installed guidance; the desktop-app microphone known-issue card, verbatim, per-session mic line and all; and the past-calls list. The old pointer card retired with the view. A call is global state: start one anywhere, switch tabs mid-call, come back to it live; ending it from Chat still files it into the conversation. Anything still pointing at the old view lands on Chat.
- Cold start: quiet by default (issue #114). Every launch opens with all three menu groups collapsed — Build 4's group-state persistence is gone, and a stale save is cleaned rather than honored — and on desktop the status column starts hidden, behind the new Status toggle in the menu chrome, its width given to the content until you open it. The toggle is not persisted either: hidden is what "by default" means. The rail itself keeps its remembered open/collapsed behavior, untouched.
- Her presence moves into the Chat window, and the column gets its door (issue #115). At the top of the Chat window sits her presence header — her name, her dot, and what she is doing: here (the same living line the status column carries), typing, in a call with you, or away when her model is stopped — computed once, from one function, so the header and the pane never tell different stories. At the header's right edge, hugging the side, sits the status column's toggle: on a desktop it shows and hides the column from the #114 ruling; on a phone the same control drives the dock — one state behind two doors, the phone bar's button and this one, and they never disagree.
- Cartridge color is family, and a knowledge pack's center is the spray (issue #117). Chips keep the coral red they have always worn — it is now officially the chip color. Knowledge cartridges wear green instead — the presence green, (52, 211, 153) — bands, header, and every derived tone following the family accent; and a pack's window no longer carries the book glyph: its center is the spray, the name-seeded tile field grown to fill the whole window, denser and brighter toward the middle. A pack author's own declared
iconart still outranks the spray. Mesh pairing cards wear a clear network blue, (59, 130, 246), in place of the old cyan, so the three families read apart at a glance. The face text block is locked in the same round: pixel-art name, the brief description, and a split footer — family and kind at the bottom left, the version at the bottom right, omitted when a picture's data does not know it. Faces are still minted at forge time — installed pictures keep the faces they were forged with. - The Docs view gets its own layout frame (issue #119). The documentation no longer scrolls as one column with the menu riding the article: the Docs view is a fixed frame now — its header still, and below it two panes that scroll on their own, the topic menu and the article, each filling the frame's height exactly. Reading to the end of a long page never buries the menu; the menu keeps its scroll position across page switches, and the open page is kept in its view. The popped-out Docs window shares the same two-pane frame.
- An update's version stamp lands last, and the Updater can say so (issue #104). The installer now carries the new bridge and VERSION under staged names and flips them only as its final act — the config's version record first, the VERSION file last of all — so a step that fails before the flip leaves every version witness reading the old build, and Check for updates honestly offers the update again instead of declaring a broken install current. And when the installer exits nonzero after the flip, the Updater verifies what is actually on disk before it speaks: a target build standing there is reported as installed, with a tail failure in the named step — the step named from the install log — not as the manual-fallback failure. The fallback text remains for the case that earns it: nonzero exit, old build still standing.
Build 4 — the menu build (issues #102, #103). The server names wear the pipe from this build: Apex | Node, Apex | Mesh, Apex | DHD — and the menu itself is grouped now. Chat stays pinned at the top, alone; under a break, Apex | Systems drops down to Node, Mesh, DHD; the everyday surfaces stay flat below it; and at the foot, Apex | Utilities opens upward with Settings, Docs, and GitHub. The groups animate — a height glide, a turning chevron, children arriving a breath apart — and all of it goes instant when reduced motion is requested.
The Memory view is retired, and its two jobs moved home: the conversation manager is now the Chats tab, fixed at the top of the status column on the Chat page — switch, start, and delete conversations and manage uploaded files without leaving the thread — and what Navi remembers from chat lives under Settings → Memories, between Voice and GitHub & Backup, with a pointer to the Memory document editor, which stays under Operator & Persona. One correction to the first notes for this build: the old view never had a rename control for chats; Build 4 keeps switch, new, and delete, exactly as they were.
Build 3 — the art build (issue #101). A chip's window was a flat field with a blocky glyph standing in it, drawn coarser than everything around it. Now the window carries a dim QR-style tile field — seeded from the cartridge's name, finder squares in three corners, at the face accent dimmed to a whisper — and the art over it is a solid silhouette drawn on the field's own grid. All sixteen built-in glyphs (key, lock, hash, text, swap, gear, network, image, music, dice, book, terminal, shield, clock, chart, star) were redrawn at 20×20 as filled objects with negative-space cuts: a lock is a body with a shackle hole and a keyhole, a die is a mass with its pips cut out. Knowledge cartridges wear the same treatment.
And authors can draw their own: a chip's navi-chip.json (or a pack's navi-rag.json) can declare an icon — 20 rows of 20 characters, # accent, o shade, . empty. Declared art outranks the built-in glyph, rides inside the picture like the version does, and Chip Studio and RAG Studio edit it as text rows. Malformed art is refused in plain words at forge and export time; a picture whose art arrives damaged falls back to its glyph — a bad drawing never bricks a cartridge. Faces are still forge-time: published pictures keep theirs until re-forged. One packaging note: this build's zip is stored uncompressed, matching every canonical package in the line (the first two 0.1.9e builds were deflate-compressed, which is why this one reads larger — the content grew by about 20 KB). Gates: 43/43 under the golden-master delta rule (Build 2's suite covers the unchanged machinery; this build tested its delta, its blast radius, and a smoke pass — Mesh pairing cards render byte-identical).
Build 2 — card-grade faces, and the picture learns its version.
- Chip faces are cards now. A new forge draws the chip's face with the same renderer the Mesh pairing cards use: the family header, the chip's glyph large in its window, then the chip's name, what it does, and in small print its version and output kind. A chip no glyph claims keeps the name-seeded pattern in the window, at the same grade.
- Knowledge cartridges match. A pack's picture face carries the same card grade — the book glyph large, the pack's name and what it holds, and in small print its version and how many documents ride inside. The payload, the 8 MB ceiling and its plain refusal are unchanged; retrieval still never reads the picture.
- The chip picture states its own version (issue #99). A forged or installed chip now carries its version inside the picture: the install receipt names it, the ChipDeck sleeve states it, and the face prints it. A picture forged before versions rode inside simply shows none — nothing guesses.
- Faces are forge-time, still. Published and installed pictures keep the faces and payloads they were forged with until re-forged. First re-forge under the new face: navi-cryptex v2.0.2.
Apex v0.1.9e — Build 1 (Edge pre-release). The 0.1.9 QOL line, bundled into one build. An Edge build is a preview cut from the dev branch, not a full release — Stable remains v0.1.8.
- The names unify (#92). The Servers group reads Navi (detail: llamafile · port 8080) / Apex Node / Apex Mesh / Apex DHD, everywhere in the app. Display only — no identifier, port, or protocol string moved.
- Chip faces are drawn, not rolled (#92). New forges wear the family shell — black body, coral bands — with one pixel glyph that means the chip's function, named in its manifest (
glyphin navi-chip.json). The old seeded pattern is fallback only; existing pictures are never redrawn. - Installs show up (#95 — fixed at the root). A NUR install said success but Local Chips stayed at 0: the dashboard's install completion cleared the catalog cache but never the workflows payload Local Chips renders from. Fixed — and Uninstall now lives on the catalog card and in ChipDeck as well as Local Chips, removing cartridge and record together. (The retired composer pill shelf makes the old missing-pill symptom moot.)
- Run chips like programs (#97).
./navi-cryptex hex -e -t "Hello world!"— or./cryptex; short names and unique prefixes resolve, bare./lists the shelf, ambiguity answers with candidates./chipremains as the alias; under the$CLI latch,./still belongs to the shell. The composer pill shelf is retired; attribution pills under replies stay. - Knowledge packs ride pictures (#93). A RAG pack exports as one knowledge-cartridge PNG (manifest + every document's text, compressed) and installs from the picture alone — its index built locally at install; retrieval never reads the picture. Over the 8 MB ceiling, the export refuses in plain words and points at the ZIP.
- ChipDeck (#94). The binder: sleeves with the actual pictures of everything that rides one — chips, Mesh pairing cards, knowledge cartridges — with state at a glance and the actions gathered (run, download picture, Studio, uninstall; pair/unpair; activate/deactivate/remove). Drop a picture on the Deck and it slots in by the payload inside it.
- Publish does everything (#98). Publish to GitHub now completes the chain: repo, files, topic, tag from the manifest version, and a published release with the cartridge picture attached. An already-released version is refused before anything uploads. (Implemented end-to-end and bench-proven with a stubbed GitHub transport — the first signed-in field publish is its live proof.)
Full notes: https://github.com/AlexKollar/Apex/blob/edge/CHANGELOG.md