Skip to content

History

Revisions

  • Jira guide: the removed search endpoint, and real-Jira proof (#953) Cloud retired /rest/api/3/search for /search/jql; DC keeps the old one. Recorded with the lesson that generalises — a connector's correctness depends on someone else's API, so when a call starts failing, suspect the endpoint before the logic. Status banner now records that the whole journey has run against a live Jira Cloud site.

    @edmozley edmozley committed Aug 2, 2026
    6aa2531
  • Tracker guide: the poll cron (#951) Records that the poll is currently the ONLY way status changes, the per-connection interval, stamping on failure, and the fetchIssues() distinction — swallow one bad chunk, but rethrow when every chunk failed, because [] is indistinguishable from 'those issues do not exist' and the poll would look healthy while the tracker was down.

    @edmozley edmozley committed Aug 2, 2026
    aecdb3f
  • Tracker guides: the ticket side (#950) §7c covers the pill in the Links strip, the manual escalate and its preview, integrationsBodyToText (and why strip_tags alone leaves a stylesheet in the description), and the two traps — modals open with classList.add('active') not style.display, and inbox.js?v= must be bumped. Both present as 'the button does nothing'. Header no longer claims 'engine only'; status now says raising works but nothing comes back yet and the poll cron is missing.

    @edmozley edmozley committed Aug 1, 2026
    943ec98
  • Bring the tracker guides up to #948 and #949 The pages documented up to #947 and then two commits shipped without them, so §9 confidently said "not built" about the settings screen and the workflow actions — both live. A wiki that is wrong is worse than one that is missing, because someone trusts it. Added: §7a the workflow surface (the two actions, the connection lookup, argBool, and why dry-run safety lives in runInner rather than the handler), §7b the settings screen (the folder-level .htaccess pattern, one shared provider.php, and the four traps — .form-row being display:flex in inbox.css, full-width needing BOTH no max-width and no auto margin, showConfirm/showToast over browser dialogs, and closest() once a button contains an SVG). Updated: the file table, the §9 status table, §10 to cover the provider registry that keeps a new tracker cheap, the contract for pollChanges()'s watch list, and a note that the service is self-contained because it once was not and fataled on decryptValue(). Recorded the miss itself in "keeping this page honest" — same commit, not the next one.

    @edmozley edmozley committed Aug 1, 2026
    1b36d0d
  • External issue trackers + Jira connector developer guides (#945-947) Two pages, split on the line that matters: the engine page is the contract every tracker must satisfy, the Jira page is the worked example. That split is the point — whoever writes the GitHub or Azure DevOps connector needs to know which decisions were forced by the contract and which were just how Jira happens to work, and both pages say so explicitly. Also cross-referenced from Multi-Tenancy-Developer-Guide: tracker connections added to the connection column of the three-meanings-of-NULL table, and the outbound twin of the membrane rule recorded next to the inbound one. Both pages carry a "keeping this page honest" section listing what must be updated in the same commit as a code change.

    @edmozley edmozley committed Aug 1, 2026
    d00486b
  • Forms: document date / time fields (#944) Forms.md — nine question types, a "Date and time fields" section covering the three modes and why it is one type with a setting rather than three types, the is_after / is_before row on the operator table plus a note that the menu narrows per trigger type, and the timezone warning. Dev guide — new §3b on date fields: the mode, the stored formats, why these are NAIVE values and must not be converted, why the operators compare as strings (ISO-8601 lexical order is chronological order), and opsFor() narrowing the menu without narrowing the API. Also: config must be MERGED not replaced (an earlier buildRulesForSave dropped date_mode), the system prompt is part of the feature and drifts silently (section was whitelisted in #940 but never described to the model, so it was never emitted), and adding datetime was the first real test of the deliberate three-renderers decision. Every app file named verified to exist; every wiki link resolves.

    @edmozley edmozley committed Jul 30, 2026
    87a26ef
  • Forms: document sections, conditional logic and field identity (#940-943) Forms.md — nine types (section added), a Sections section, a Conditional questions section with the operator table and the three deliberate behaviours (hidden is not required; hidden answers are not stored; conditions look backwards only), and what "removed" means on a submissions column. New Form-Sections-and-Conditional-Logic-Developer-Guide.md — the colour-keyed file table, why field identity had to be fixed first (with the old positional sync quoted), the three identity spaces (id / "idx:N" / _key), why a section is a row rather than a table, the backwards-only rule as the thing that makes cycles impossible by construction, the two evaluators and the one case table that keeps them honest, soft delete, version-fork remapping, why the three renderers were deliberately NOT merged, the AI-apply trap, and the upgrade note for REST clients. Every app file named verified to exist; every wiki link resolves.

    @edmozley edmozley committed Jul 30, 2026
    9801e78
  • Knowledge Assistant: user page + developer guide New pair for #939. The user page leads with why the assistant is allowed to refuse, since that is the design decision the whole feature rests on. The developer guide carries the colour-keyed file table and documents the three things nobody would guess: the buffered verdict line, overlap-coefficient similarity with a two-token floor (not Jaccard), and single-linkage clustering - the last two both forced by tests/knowledge-gaps failing. Also a section on Knowledge.md and both pages in _Sidebar.md.

    @edmozley edmozley committed Jul 29, 2026
    35385e5
  • New: Asset scanning — Developer Guide (the deep dive) The scanning journey was spread across three pages and mostly implied. This traces it end to end: how a printed square of ink becomes an updated database row. Twelve sections, the substantive ones being: - Three scanning surfaces (native camera / in-app scanner / USB gun) and why none of them is redundant. - The public base URL — the single most consequential line in the feature. Why it must not come from the request, the localhost banner, the sub-folder double-append that would otherwise be printed onto physical labels, and the two tunnel traps (a rotating free ngrok URL, and its browser-only interstitial that curl won't show you). - The resolve flow, including why three regexes deliberately disagree (routing is permissive, validation is strict, dispatch is neither). - The write path: one door, shared with the desktop editor, so a phone edit gets the same audit trail and warranty sync — and why the no-op skip is load-bearing both in production and in tests. - The security model: the token is a name, not a password, and the unknown-vs-another-company answers are identical on purpose. - Two mobile strategies in one module — mobile-FIRST scan pages versus the mobile-ADAPTED module — and the five-step hinge where they meet, which is what makes a scan land on the detail pane with the device Back button working. - How to test a camera headlessly (Y4M fake camera) plus the four traps hit doing it. - Failure modes, each mapped to its actual cause. Section 1b of the labels guide now points here instead of duplicating it; cross-linked from both QR pages, Mobile: Assets, and the sidebar.

    @edmozley edmozley committed Jul 29, 2026
    18cb14b
  • Asset QR labels: document the in-app camera scanner (#938) Analyst page: a "Scanning a lot of labels" section — the two modes, the already-set/counted-once behaviour, undo, serial fallback, torch, and the https caveat stated plainly. Developer guide: scanner.php / resolve_scan.php / vendored jsQR added to the file table; a new 1b explaining why the scanner sits next to scan.php and the decoded-text -> endpoint flow; the schema-gate table extended; and the verification section gains the fake-camera method (Y4M into --use-file-for-fake-video-capture), the duplicate-count bug it caught, the transaction-based cross-company test, and the two headless traps hit on the way.

    @edmozley edmozley committed Jul 29, 2026
    b34102d
  • Mobile: Assets page, plus overview/tickets/sidebar updates New Mobile-Friendly-Assets.md covering rounds 1 (#936) and 2 (#937): scope table, what's done, the card-feed vs contained-scroller decision and how to choose, the iOS reflow trap everything is dodging, the restructured mobile.js entry point, the layer map, challenges & solutions, and how it was verified headlessly. Overview refreshed: Assets added to the module status table, LAYERS 1-13 -> 1-15, opt-in example bumped to v=31/v=14, the "can't self-verify" line replaced with the harness that now works (and the 360px-iframe / measure-scrollWidth / desktop-positive-control method), "one module deep" -> two. Tickets page: shared-file version bump warning + sibling link. Assets.md: an "On a phone" pointer. Sidebar: the new page.

    @edmozley edmozley committed Jul 29, 2026
    f9d345f
  • QR asset labels (#935): user page + developer guide A pair: there is real user-facing depth here (printing, scanning, using a print house, and the reverse flow for pre-printed tags) as well as a lot underneath. Asset-QR-Labels.md — the two halves, asset tags and why they're unique per company, printing one or a batch, what scanning gives you, handing a job to a professional print house (variable data printing), and assigning pre-printed tags by scanning. Asset-QR-Labels-Developer-Guide.md — the two-identifier design and the question that forces it; why per-company uniqueness CANNOT be a unique index (NULL tenant_id defeats it, and an index that silently doesn't apply is worse than none); why the URL is short; the schema gates and which one is critical; why scan.php is mobile-first rather than mobile-adapted, and why that's a deliberate departure from the wrap-don't-edit rule; the print sheet and CSV; the tagging loop; and the batch selection. It also writes down four bugs found building it, each with the lesson rather than just the fix: PHP casting numeric-string array keys to ints (a dropdown disagreeing with its own page), fputcsv's deprecated $escape writing HTML into a download, the open row having to be IN the selection, and text selection starting on mousedown — which is why a user-select class applied on click can never stop the first shift-click. Assets.md gains a section pointing at both; _Sidebar.md lists them.

    @edmozley edmozley committed Jul 28, 2026
    e8b4207
  • Collision detection (#934): user page + developer guide A proper pair this time — unlike snooze, there is real user-facing behaviour to explain: what the two states mean, and why the thing deliberately doesn't stop you sending. Collision-Detection.md — what you see, the reply-window warning, why it warns and never blocks, and what it deliberately isn't (it is NOT a record of who read what; that information is overwritten every few seconds and thrown away). Collision-Detection-Developer-Guide.md — the table and the three decisions behind it (the unique key that makes the heartbeat an upsert; why ON DELETE CASCADE is right here when it is wrong almost everywhere else in this schema; ordering in freeitsm.sql), heartbeat- not-session with a table of what happens on a crash / dropped network / lost leave, why heartbeat and read are one endpoint, the three client details that are easy to get wrong (hidden tabs, the late response that must not paint, composing beating immediately), why a lock would be the wrong design, how it was verified, and how to extend it. Also records the two testing traps that cost the most time: a virtual-time browser races its timers ahead of external curl, so timing tests must observe in page time; and contentWindow.someLet is undefined because top-level let never lands on window. Tickets.md gains a short section pointing at both, and _Sidebar.md lists them under Tickets.

    @edmozley edmozley committed Jul 28, 2026
    1dbf0b7
  • Tickets: rewrite the right-click menu section — it listed 2 of 15 actions The section still said "Two actions for v1" (Link CMDB object, Record time) and had been overtaken by everything since: the five triage flyouts, three more link targets, change subject, merge, move to company, snooze and wake. Now grouped by what you are trying to do (triage / linking / time / getting it out of the way / housekeeping), with the parts that are easy to get wrong written down: which items appear only when they make sense and why, that every flyout except Set status opens with a row that CLEARS the field, and Outlook's rule for whether the menu acts on one ticket or the whole selection — which is how a bulk action ends up hitting the wrong set. Also names the two other right-click surfaces (the Trash folder, and a message header opening Split). Checked against the running app rather than the source alone: the rendered menu was enumerated for both a single ticket (Merge hidden, Change subject shown) and a two-ticket selection (the reverse).

    @edmozley edmozley committed Jul 28, 2026
    bc7ab44
  • Snooze (#933): brief user section on Tickets, full developer guide Snoozing is small to use and large underneath, so the analyst-facing description is a section on the existing Tickets page rather than a page of its own, and the detail goes in a dedicated developer guide. Tickets.md — new "Snoozing tickets" section, plus the three places that were now incomplete: the right-click menu list, Reopen on Customer Reply (the same four doors also wake a snoozed ticket), and Settings → General (start of the working day). Snoozing-Tickets-Developer-Guide.md — the schema and why there is no table, why waking is the clock rather than a cron and what that forbids (never test IS NOT NULL for "asleep"), the two predicates and why the "only sleeping" one degrades to 1=0 rather than an empty string, the schema gate that keeps the inbox alive before Database Verification has run, server-side preset resolution and the rule that a preset is dropped when its name would stop being true, the four inbound doors, what snooze deliberately doesn't touch (SLA, search, updated_datetime, status), why it is on the ticket rather than the analyst, how it was verified, and how to extend it.

    @edmozley edmozley committed Jul 28, 2026
    347d334
  • Command palette: user + developer wiki pages (#932) - Command-Palette.md — analyst guide: ⌘K/Ctrl-K, jump-to-module, the seven search sources, keyboard map, scope/access notes. - Command-Palette-Developer-Guide.md — injection via renderWaffleMenuJS (the global $modules gotcha), the two-gate aggregator, per-source tenancy table, the CHG-#### trick, deep links, verification, how to extend. - _Sidebar.md — linked both under Getting Started.

    @edmozley edmozley committed Jul 28, 2026
    d64da95
  • Catalogue request approvals: analyst + developer pages (#928/#929) New pair: - Catalogue-Request-Approvals — set up an approver, approve/reject, the requester's dashboard view, who can approve, worked example, limits. - Catalogue-Request-Approvals-Developer-Guide — colour-keyed file table, the gating rule, the auto-raise (promote-to-ticket), the three surfaces, verification, and extension notes (manager routing, board, reasons). Updated Portal-Request-Catalogue: removed the now-false "no approval step" and "promote-to-ticket coming" claims, cross-linked the new pages. Sidebar: added the pair under Forms.

    @edmozley edmozley committed Jul 23, 2026
    589c626
  • Splitting: individual-message selection (#926) Analyst page: the "what moves" flow is now a checklist of every message with This-and-newer / All / Clear helpers, not "this one + newer". Drops the "cannot tick arbitrary messages" limitation. Developer guide: splitMessagesByIds + the email_ids path, the marker-aware emptiness guard (splitMarkerEmailIds / splitMovableCount), list_all preview mode, .split-pick-* CSS, the cache-buster gotcha, and arbitrary-selection marked done with notes-splitting as the next step.

    @edmozley edmozley committed Jul 23, 2026
    6e4cf31
  • Add Demo Data user + developer wiki pages - Demo-Data: what it seeds (incl. the RBAC example), what import does, tips. - Demo-Data-Developer-Guide: the tier/@ref/token model, string->id lookups, transaction/repeatability guarantees, and a step-by-step for adding demo data to a new module. Colour-keyed file table; links the D001 diagnostic. - Sidebar entries under System.

    @edmozley edmozley committed Jul 22, 2026
    ad13a4a
  • HTTPS certs: reframe as optional — lead with includes/cacert.pem, php.ini is Option B (#919)

    @edmozley edmozley committed Jul 22, 2026
    87cdeef
  • SSL guide: note the setup checklist's live verification probe (#920)

    @edmozley edmozley committed Jul 22, 2026
    d78a0d4
  • SSL guide: add docker/config.php to the before/after table (#919)

    @edmozley edmozley committed Jul 22, 2026
    26e5ee8
  • Add "SSL Verification — the Global Setting" dev guide (#49/#919) Documents the problem (insecure default, scattered per-module toggles), the empirical findings (only CURLOPT_CAINFO works — putenv/ini_set don't), the design (one global switch + sslApplyCurl helper + shipped cacert.pem), the pitfalls (vCenter self-signed, two php.ini, never hand-write VERIFYPEER), and a before/after table of every file changed. Also update HTTPS-Certificates-and-CA-Bundles: the app now ships a bundle so a fresh install verifies out of the box, and there are no per-module Verify SSL toggles. Add the new page to the sidebar.

    @edmozley edmozley committed Jul 22, 2026
    286628c
  • Splitting tickets pair + undo on both merge and split (#914–#916) New: Splitting-Tickets + Splitting-Tickets-Developer-Guide, same format as the other pairs — colour-keyed file table, the reasoning, and the traps. The dev guide leads on why split is NOT the mirror of merge: a merge protects a reference the customer already holds (pointer + inbound redirect), a split mints one nobody has ever seen (no pointer, no redirect, both tickets live, linked `related` not `duplicate_of`). That table is the thing worth reading. Also written up where they will be read: - the empty-ticket guard, and why the preview and the split share one function - the thread renders NEWEST-FIRST, so every string says "newer" not "after" - the control was hover-only and nobody could find it; a control nobody can find is a missing feature, not a subtle one - splitPlural() exists because "1 message(s)" reads as a bug Merge pages updated for undo, including the contrast that matters: undo-split REFUSES on any activity (the new ticket was brand new), unmerge deliberately does NOT (the survivor is the live ticket and collects replies within the hour). Plus what unmerge records and why the source's previous status must be stored rather than inferred. Every file, function and column named in the tables verified to exist; every internal link resolves. Linked from the sidebar under Tickets and from Tickets.md.

    @edmozley edmozley committed Jul 21, 2026
    9059a24
  • Merging tickets: analyst page + developer guide (#912) Merging-Tickets.md — doing a merge, what happens to the reference and why that matters, the AI briefing, the two settings and their trade-offs (including the search trade-off on HTML-only), whether the HTML attachments are safe to open, and the honest edges (can't be undone, 20 max, same company, no split yet). Merging-Tickets-Developer-Guide.md — colour-keyed file table, then the four decisions: - the SDREF in every past notification is why a merged ticket is never deleted, and why the pointer is a column and not a status name - what moves vs what stays, with the test that decides it ("does this row describe the conversation, or what happened to THIS ticket?") - access checked for every ticket before a single row moves; loadTicket made public rather than copying the scope rule; one transaction - the snapshot is safe because of THREE things, and the one that matters most is Content-Disposition: attachment — it never renders on the app's origin Includes both bugs found on live data after shipping, written up where they will be read: tickets.tenant_id NULL means the DEFAULT company (comparing raw values refused ordinary merges), and only Anthropic streams tokens so an AI progress indicator must never depend on tokens arriving. Every file, function, constant and CSS class named in the tables verified to exist; every internal link resolves. Linked from the sidebar under Tickets and from Tickets.md.

    @edmozley edmozley committed Jul 21, 2026
    804adef
  • Bulk actions: analyst page + developer guide (#910) Bulk-Actions.md — the key map (mouse and keyboard), why plain click starts again, the anchor behaviour that lets a second Shift+click SHRINK a block, the three display modes, and the deliberate edges (desktop only, 100 per batch, subject not bulk-editable). Bulk-Actions-Developer-Guide.md — colour-keyed file table first, then the two decisions that matter: - three selection variables, not one: the anchor is separate so Shift can measure from the last PLAIN click, and the cursor is separate so Ctrl+arrow can move over rows that are not selected (which is what makes Space work). Also why .kb-focus is an outline and never a fill. - bulk writes LOOP TicketsService::updateTicket server-side rather than one UPDATE ... WHERE id IN (...), because changing a ticket is not a column write. The failure mode that buys: a workflow that fires for one ticket and silently not for fifty. Per-ticket access control and correct per-ticket audit fall out for free. Plus the n <= 1 choke point (a real shipped bug: the bar kept saying "5 selected" while one was held), why showToast can't carry progress, the inside/outside rule shared by right-click and drag, why the drag ghost uses real child elements rather than pseudo-elements, and how the whole thing was verified with real modifier-key MouseEvents in headless Chrome. Every file, function, variable, constant and CSS class named in the tables was checked to exist in the repo, and every internal link to resolve. Linked from the sidebar under Tickets and from Tickets.md.

    @edmozley edmozley committed Jul 21, 2026
    14f4550
  • Canned responses: analyst page + developer guide (#909) Canned-Responses.md — using the Templates button, the merge-code table, saving/editing your own from the reply box, what shared templates are for, multi-company scoping, and an honest list of what v1 does not do. Canned-Responses-Developer-Guide.md — the colour-keyed file table first, then the parts that are easy to get wrong: - two nullable columns in one row meaning two DIFFERENT things (analyst_id = shared-with-team, tenant_id = the config-list meaning, not the scoped-data one) - why the permission had to split by SCOPE rather than by table, and the check-the-scope-being-SAVED rule that stops an analyst promoting their own draft into the team list - reuse the email templates' merge-code vocabulary, but NOT their raw resolveMergeCodes() — the values come from a stranger's From header - why the body is deliberately not sanitised - how it was actually verified (upgrade path, refusals paired with positive controls, a real XSS payload, headless-Chrome parse-check with a negative control, D005) Every file and function named in the tables was checked to exist on disk, and every internal link to resolve. Linked from the sidebar under Tickets and from Tickets.md (feature section + the settings-tabs list).

    @edmozley edmozley committed Jul 21, 2026
    107535f
  • Root-Folder-Tidy: document the root tidy + canonical /login URL New page covering every move (auth/, tickets/csat/, scripts/, tests/), the URL contract (/login canonical, legacy 301s, OAuth callbacks unchanged externally), the MultiViews story, the guard .htaccess pattern, and rules for future root-level pages. Linked from the sidebar under Architecture; the Architecture directory tree and file-locations table updated to match. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

    @edmozley edmozley committed Jul 21, 2026
    2d6200b
  • Absorb the README's deep-dive content ahead of the README slim-down The project README is being reduced to a landing page; every fact it held that the wiki lacked is merged here first (615 lines across 33 pages): the full email-threading & reply-flow design onto Tickets, directory layout + module-page pattern + file-locations reference onto Architecture, per-endpoint tables onto API Reference, MFA login flow + LDAP subsection + injection/audit notes onto Security, the icon library and editor internals onto Network Mapper, Branding + landing-page and help/search internals + debug-tool depth onto System, planned objects onto CMDB, custody/locations/warranty onto Assets, and more. Also corrects stale claims found during the audit: the Knowledge audience table still said customer-visible articles had no reader (the portal Knowledge section shipped), a pre-#903 Portal-Privacy ambiguity row, the Workflows trigger-wiring section (catalogue spans every module now), the superseded System module-access bullet, the Multi-Tenancy users page marked "Planned" for a shipped feature, and the locale count (21 languages incl. Ukrainian, not 20). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

    @edmozley edmozley committed Jul 21, 2026
    1a1e5a4
  • LDAP wiki: login-decision flowchart, and every fix from a code audit Adds the diagram that was missing: a mermaid flowchart of how the login decides between local, directory and single sign-on — the two moments the decision happens (leaving the first box, then pressing Sign in), all four outcomes including the OIDC-pinned rejection, the group gate, and which directories get tried for an unknown user. Audited both pages against the code. Corrected: FALSE - "db_verify only ever ADDS columns, never MODIFYs" — true of the $schema array, not of db_verify; there are seven probe-then-MODIFY precedents, two of them added last week. The page then contradicted itself by documenting those two. Replaced with the directional rule: relaxing is safe, tightening isn't. - A dead line reference (db_verify.php:2614 — the file is 2557 lines) and the wrong path for $schema (it lives in includes/db_verify_schema.php). - "covered by an explicit unit-style assertion" — no such test existed. Rather than soften the sentence, the test now exists (tests/ldap/), and the note says to go and look before writing that phrase again. - "an account with no email -> JIT must refuse cleanly" — it provisions them; that is the whole point of #872/#902. - "set default_modules so auto-created people aren't handed the keys" — they were handed the keys. Now fixed in code (#904) and documented, including that existing accounts are not retroactively restricted. INCOMPLETE - The two logins were described as identical. They differ in five ways; there is now a table. Includes the wrinkle that the analyst lookup is by username only, so an LDAP analyst typing their email takes the JIT branch. - The immutable-ID promise omitted the DN fallback, which has exactly the orphaning consequence the sentence says can't happen. - The empty-login guard, the retained-secret-on-protocol-switch behaviour, the LDAPS and phpldapadmin ports, NOCOMPLEXITY, and the OpenLDAP half of the seeding (missing it produces trap 3's misleading error). - w.noemail's NW-Sales membership, which is what makes the portal journey testable at all.

    @edmozley edmozley committed Jul 20, 2026
    2ba2475