Skip to content

History

Revisions

  • Point the Updates links at updates.php The wiki still linked to the retired updates.html on every page via the sidebar, plus once on Home. That stale page is what search engines and LLM crawlers were finding and quoting, so the project looked like it had not shipped anything since 30 May 2026.

    @edmozley edmozley committed Aug 4, 2026
    d98f639
  • Issue trackers: workflow rules work the same for both (#967) Ed asked whether the workflow module can drive Azure DevOps like it does Jira. It can — you pick a CONNECTION, so the tracker follows from that and nothing about writing the rule differs. The page now says so, since "raise it in Jira" implied otherwise. Added, because they are the two ways a rule can be written and still be wrong: - leave Project and Issue type BLANK and the connection's mapping decides, which sidesteps the next point entirely; - ⚠️ if you fill them in they mean different things per tracker — Jira wants the key, Azure DevOps the name — and Azure DevOps issue types come from the project's template, so a rule hard-coded to Bug works against an Agile project and fails against a Basic one in the SAME organisation. Also noted that the tracker triggers can branch on which tracker fired them, so per-tracker rules are possible rather than one rule having to serve both. Neutralised the last two Jira-shaped paths in shared sections (the mapping screen's location, the dry-run note). Verified no dead in-page anchors. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

    @edmozley edmozley committed Aug 2, 2026
    c400994
  • Issue trackers: make the analyst page genuinely cover both (#964) Ed asked "what should the project key be if I send it to devops" — and that question was the review. The page defined project key as "the KAN in KAN-6", which is not merely Jira-flavoured wording but ACTIVELY WRONG for Azure DevOps, where there is no key at all and the project NAME is the identifier. The connector page shipped with the connector; this is the analyst half catching up properly rather than having a DevOps preamble bolted onto a Jira walkthrough. - "Which project?" is now a two-row table, because the answer genuinely differs: Jira wants the KEY, Azure DevOps wants the NAME, and getting it wrong produces a permission-shaped error that reads like bad credentials. - "Which issue type?" gains the per-PROCESS table. ⚠️ Two projects in ONE Azure DevOps organisation can disagree about whether Bug exists — Basic has no Bug at all — so "raise it as a Bug" is not a safe default the way it reads on Jira. Also documents that a blank type becomes a Task, because Task is the one type in every process. - The connection table is now Jira | Azure DevOps side by side, which is where the missing email field and the extra Resolved setting become obvious rather than needing prose. - Neutralised the shared prose that quietly assumed Jira: the scheduled check, the multi-company rule, the never-blocks-the-ticket note and the comments section all describe behaviour that is not Jira's. Kept Jira-specific what genuinely is — the Atlassian token steps are scoped with a marker rather than generalised into uselessness. Verified: no dead in-page anchors after renaming "Comments from Jira", no dead page links. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

    @edmozley edmozley committed Aug 2, 2026
    749d0be
  • Issue trackers: Azure DevOps connector, and the contract test it settled (#964-966) New page: Azure-DevOps-Connector-Developer-Guide, the sibling of the Jira one. Issue-Trackers gains Azure DevOps setup and the Resolved setting; the two guides that PREDICTED this work now record how it turned out. The engine guide committed to a falsifiable claim before Jira shipped: adding connector #2 must touch nothing but the provider, one registry row and its settings partial. It held, and the page now says so with the three things that did change for everyone: - settings_fields, and why it is separate from credential_fields — a dropdown among the credential boxes inherits their blanking rule and silently resets on every save; - integrationsAbsoluteUrl(), because the ticket link every connector puts in an issue was built from BASE_URL, which is a path, so it 404'd from inside any tracker and had never worked; - $target may be keyed either way — core's neutral `issue_type` or the provider's own word — because reading only one silently creates the wrong kind of work item. The Jira guide's "if you are writing the next connector" list is now marked as validated, with two of its decisions reclassified as Jira-ONLY now there is something to compare against: - relative JQL dates exist because Jira reads an absolute date in the USER's timezone. Azure DevOps honours an explicit UTC Z. Copying either across is a bug, and both are right for their own tracker; - flavour-dependent author_identity is an Atlassian quirk, not a general requirement. What IS general is that it match testConnection(). What the new page records that the code cannot say for itself: - ⚠️ which field holds the body depends on the work item TYPE. A Bug's form shows Repro Steps and not System.Description, so writing the description to the obvious field returns 200 and produces a work item that looks empty to the developer who opens it; - 🔑 five state categories, not four, and why the fifth is a per-connection SETTING rather than a mapping — plus the live vindication of "never branch on a status name": "Resolved" is category Resolved on a Bug and InProgress on a User Story, same project; - ⚠️ timePrecision=true is a query parameter, not a body field, and the tempting fix for the error it throws silently re-reads a day of comments; - ⚠️ echo suppression must use createdBy.id — the two endpoints report DIFFERENT descriptor formats for the same person, so comparing those never matches and every comment we write comes back as a developer's; - ⚠️ attachments are two steps and step one attaches the file to nothing; - ⚠️ a rejected token answers 203 with a sign-in page, not 401 with JSON. Also corrected two stale rows in the analyst page's "not there yet" table: attachments and "tell the requester when it is done" both shipped weeks ago and were still listed as missing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

    @edmozley edmozley committed Aug 2, 2026
    a43d94e
  • Forms: lookup fields — analyst page + developer guide (#962/#963) New page: Forms-Lookup-Fields-Developer-Guide. The security surface earned it its own guide rather than another section in the sections & conditional-logic one — three gates, a scoping rule whose NULL means the opposite of Knowledge's, and a bug that only showed up under a live run. Forms.md gains a Lookup fields section (and the type count moves from nine to ten, which the page had been getting right by hand). What the dev guide records that the code cannot say for itself: - Why one type with a source, not one per source. Same argument as the date field's mode, and it will come up again for the next type: field_type cannot change once a field exists. - The registry IS the SQL whitelist — those column names are interpolated, so they may only ever come from the constant. Four of five column sets assumed while building were wrong, and a wrong column name renders as "no matches" rather than an error, so a source can be broken and look merely empty. - Why contracts and software are absent, with the specific schema gap each is waiting on. - The three meanings of $tenantIds, since null / [] / [ids] are all legitimate and confusing two of them is silent. - ⚠️ NULL tenant means "the Default company's" here, the opposite of Knowledge — and on a single-company install every row is NULL, so the clause handling it is not an edge case, it is the feature working. - The #963 post-mortem, because the generalisable part is not the fix: a function_exists() guard around a security rule degrades silently, and not necessarily toward the safer behaviour. Here it degraded toward refusing legitimate answers. - That adding a field type means five places, one of which is PROSE — and that the prompt had already drifted, omitting datetime and section for months without anything failing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

    @edmozley edmozley committed Aug 2, 2026
    4799f0b
  • Issue trackers: sending an attachment after escalation (#961) Records why it is a button rather than automatic, and the guards on an endpoint that takes two ids from the browser — chiefly that the attachment must belong to that ticket, or any file in the system could be posted to any issue.

    @edmozley edmozley committed Aug 2, 2026
    546ac3c
  • Issue trackers: attachments proven live, and refresh the stale header A real 83 KB PDF went from a ticket to KAN-6 and Jira reported it back byte-identical, so 7g is no longer 'unproven live'. The page header had drifted two releases behind: it still said tracker.* triggers did not exist and quoted 252 assertions. Now #945-960, 288 assertions, 12/17 parity, with what is genuinely still missing listed.

    @edmozley edmozley committed Aug 2, 2026
    3d8805b
  • Issue trackers: attachments (#960) Gains 7g: why inline images are excluded (and what that costs), the size/count rules, why a failed upload must never fail the escalation, and the three Jira specifics that make an upload work — the no-check token, the field named `file`, and a random boundary. Also records a test lesson: asserting the bytes AROUND a sanitised filename fails on correct output. Assert the value.

    @edmozley edmozley committed Aug 2, 2026
    3a44bad
  • Time-Based Triggers: these had never actually fired (2026-08-02) The page described the design correctly; the code did not match it. A duplicate sla_format_minutes() fataled the SLA cron on invocation, so sla.warning and sla.breached had never fired since the feature shipped on 17 May — with two further wrong-column bugs hidden behind it. Recorded here because the page is what someone will trust when adding the next one: a trigger being registered, documented and visible in the editor proves nothing about whether it fires. Make it fire.

    @edmozley edmozley committed Aug 2, 2026
    8a2d9f8
  • Issue trackers: tracker.* triggers (#958), and why they are not time-based Engine guide gains 7f: the three triggers, that each carries the ticket, the aliased columns in the payload query that are silent when wrong, and the comment loop that terminates only because send_note_to_tracker records what it pushes. Time-Based Triggers gains a "a cron is not the same as time-based" section, because reusing the emissions ledger for these is the obvious instinct and it is wrong — a fingerprint on current state would swallow a genuine third transition.

    @edmozley edmozley committed Aug 2, 2026
    8445e85
  • Issue trackers: separate the two "no change to the tickets module" claims Escalation genuinely added nothing to the tickets module and that still holds. The module as a whole stopped being untouched at #954, because reading comments back means rendering them — get_notes.php, inbox.js, inbox.css and tickets/index.php are all in it now. Worth pinning because the stale version is exactly the sort of line that ends up in published copy.

    @edmozley edmozley committed Aug 2, 2026
    14378a5
  • Issue trackers: the pretty-URL relative-path trap on the help page Three traps worth naming, all hit within minutes of adding the page: a pretty URL deeper than its file breaks relative asset paths (use BASE_URL); $path_prefix must stay filesystem-relative because the system header requires through it; and a System page must use the system header, not the root one, or it renders the ticketing nav.

    @edmozley edmozley committed Aug 2, 2026
    71222c0
  • Issue trackers: the grouped routing rules and the in-app setup guide Records why departments and companies are never one flat list (the grouping is what makes precedence visible), and documents help.php — including that its rewrite must precede the provider rule.

    @edmozley edmozley committed Aug 2, 2026
    29b767e
  • Issue trackers: document mapping (#957) Engine guide gains 7e: the precedence rule and why it is that way round, why priority has no wildcard, the narrow rejected-priority retry, the namespaced local_key, and two traps — the column is ticket_type_id not type_id, and a shared service must not assume it owns the transaction. Analyst page gains a Mapping section in plain terms, and the Jira page records that mapping needed no connector change beyond listPriorities(), which is the abstraction doing its job.

    @edmozley edmozley committed Aug 2, 2026
    c5aee9b
  • Add a Scheduled tasks page; bring the tracker dev guides current New page: Scheduled-Tasks. Ed asked for one place covering every cron job / Windows task FreeITSM needs, because "you must schedule something or comments never arrive" was buried in the Jira page. Covers all four jobs, what silently breaks without each, Windows and Linux commands, how to run one by hand, and how to tell whether they are working. Also documents what is NOT a scheduled task, so nobody goes looking for a job that does not exist or schedules the InTune workers, which the app starts itself. Notes that mail is fetched by the browser, so nothing is collected overnight with every browser closed. Windows gotchas that cost time today: /ru SYSTEM tasks are invisible to a non-elevated session, 0x41303 means "not yet run", and the escaped quoting fails if a path has spaces. Linked from Home, the sidebar, and five places in Issue-Trackers. Dev guides brought current for #954-956: assertion count 227, range #945-956, the pipeline diagram no longer shows the removed author guard, and a new note on inbound_enabled — a column that shipped in V1 with no writer, so the feature it gated could never be turned on. That is a trap worth naming for the other columns V1 shipped early.

    @edmozley edmozley committed Aug 2, 2026
    06e4b2b
  • Issue trackers: why author-based echo suppression was removed (#956) Records the failure and the reasoning, at length, because the idea is tempting enough that someone will reach for it again: "authored by our account, therefore our own echo" assumes a service account that never types, and the token owner is usually a human who comments. Also notes the uncomfortable part — the suite was green and contained assertions pinning the wrong behaviour, because they shared the design's assumption. Only a live account found it.

    @edmozley edmozley committed Aug 2, 2026
    fb7f808
  • Issue trackers: say what "the check" is, and where the tickbox lives Both raised by Ed while setting this up for real, and both were fair. "Run the poll" was jargon that meant nothing to a user. The page now explains the check in plain terms — FreeITSM asks Jira rather than Jira calling us — says that nothing on the page works until it runs, and shows how to run it once by hand for testing. The Accept updates tickbox is inside the connection, not on the list page, so the page now spells out the pencil-then-scroll route, notes the token box can stay blank, and adds both to troubleshooting.

    @edmozley edmozley committed Aug 2, 2026
    dbece7d
  • Issue trackers: author_kind, and why "Unknown" was the wrong answer (#955) Records the four cases a note's author can be in, and that collapsing them into one label discards information the row holds. Also notes that delete_analyst.php reassigns nothing, so a departed analyst's notes are the normal case rather than an edge case.

    @edmozley edmozley committed Aug 2, 2026
    c027ddb
  • Issue trackers: comments coming back, and the join that hid notes (#954) The engine guide gains 7d — the inbound pipeline, both echo guards, the "first poll imports nothing" rule and why the comment map row is written before the note (the unique key is what wins a race between two cron runs, not the check before it). The Jira guide gains 7: two calls rather than one per issue, why the JQL date must be relative (an absolute one is read in the Jira user's timezone, not UTC), why the ADF reader lives in the connector rather than IssueDoc, and that author_identity must match testConnection() or echo suppression silently stops working. The analyst page documents the toggle, that imported comments are always internal, and that enabling it does not bring back a backlog. Also recorded: get_notes.php inner-joined notes to their author, so 10 of 17 notes on the dev install were invisible. Both dev pages now list what forces an update, including the rules above.

    @edmozley edmozley committed Aug 2, 2026
    de457a2
  • Tracker guide: correct the shipped range, point at the parity scoreboard

    @edmozley edmozley committed Aug 2, 2026
    4603699
  • Issue trackers: the analyst page, and cross-links (#945-953) The dev pair existed but the analyst page did not, so a user had no page at all. Issue-Trackers covers setup (including the API token living in the Atlassian account, not Jira), raising an issue, reading the pill, automating it with a workflow rule, and an honest 'what is not there yet' — comments coming back is named as the biggest gap rather than left to be discovered. Also: sidebar entry, and both dev pages now point at it.

    @edmozley edmozley committed Aug 2, 2026
    ebe7f6a
  • 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