Skip to content

Releases: bharathvbcr/DevType

DevType v1.2.0

Choose a tag to compare

@github-actions github-actions released this 02 Oct 12:46

DevType 1.2.0 — Structured refusals, honest recovery, voice remedies, and a preview that answers

The GitHub artifact follows the project's existing ad-hoc signed, unnotarized distribution policy. Gatekeeper may reject it. Local packages use the available Apple Development identity.

Expansion refusals and recovery

InjectOutcome.refused carries a diagnostic reason string. Previously, the pipeline's internal branch identifier (erasePrecondition, entryGate_secureInput, axOnlyRange, etc.) was consumed by PermissionCoordinator.sanitizedRefusalReason to generate an English sentence and then discarded. Any caller needing to understand what happened had to match substrings against that English sentence.

That approach was fragile and lossy:

  • Most refusals matched no branch and fell back to the generic "click into a normal text field (not a password field)" guidance — even when the user was already in a normal text field (such as an erase-precondition check) or when the actual cause was a missing Post Events permission.
  • Relying on English prose also broke across localizations.

DevType 1.2.0 introduces InjectRefusalKind, providing a single, strongly-typed classification for injection refusals:

  • Canonical classifier: InjectRefusalKind.classify(reason:path:) is the single owner of the (reason, path) -> (kind, sentence) mapping. The human-readable sentence and the typed kind are derived from the exact same branch and can never diverge.
  • Dedicated recovery guidance: PermissionRecoveryController uses the typed refusal kind to display accurate, localized guidance for every specific failure scenario instead of generic fallback text. It no longer blames Accessibility when Accessibility is already granted.
  • Honest menu actions: The "Expansion Failed — Restart Engine" menu item is now filtered by whether restarting the engine could plausibly change the outcome (warrantsEngineRestart). Guards that correctly and safely refused an expansion (such as erase preconditions, target application changes, or active Secure Input) leave a healthy engine behind. Offering a restart in those situations misled users into opening Permission Recovery only to find all permissions granted. Restart is now reserved for unknown or silent failures.

Voice dictation remedies

  • Typed remedy keys: UserAction now defines a typed remedyKey localized across English, Japanese, and Korean.
  • Actionable failure copy: Failures carrying a specific remedy (such as manifestWriteFailed requiring disk space) now surface actionable user instructions ("Free up disk space on your Mac") rather than collapsing into generic session failure messages.

Documentation and website

  • Refactored documentation site with modular styling (docs/assets/site.css) and interactive scripts (docs/assets/site.js).
  • Updated comprehensive guides for user workflows, permissions, voice dictation, development, and system architecture.

Accessibility write capability and condemnation

  • Non-mutating verdict queries: AXWriteCapabilityStore.verdict(for:role:) and diagnostic report capture are now non-mutating queries. Asking whether an application allows AX reads on the paste path or generating a diagnostic report no longer spends the one-shot re-test earned by an application build update. The one-shot re-test is reserved strictly for shouldSkipAXSelectedText when an AX write is actually about to be attempted.
  • Unstable AX role erase recovery: Combo boxes, menus, and lists report a selected item as AXValue rather than the typed buffer. Erase precondition checks now route vouched typed triggers in unstable AX roles to HID erase instead of refusing the expansion when AXValue does not match.
  • Read-only and projection role erase recovery: Elements that do not accept text mutation (such as terminal grids or static projections) report rendered representations rather than typed buffers. classifyAXValue classifies these projections so vouched triggers degrade to best-effort HID erase instead of refusing the expansion, while preserving strict refusal for real text fields.
  • Focused role diagnostics: Diagnostic reports and mismatch reasons now record the focused kAXRoleAttribute so field reports can distinguish genuine text changes from projection mismatches.

Synthetic paste delivery: typing fallback for Electron editors

In Cursor, VS Code, and other Electron-based editors that bind ⌘V to internal command handlers (document.execCommand("paste")), synthetic paste events can be silently swallowed. The trigger was erased, but the expanded text was never inserted.

  • Swallowed synthetic paste detection: AXWriteCapabilityStore.swallowsSyntheticPaste(bundleID:) identifies applications in the VS Code and Cursor family that swallow synthetic paste events.
  • Unicode keystroke injection fallback: For affected editors, TextInjectionPipeline.insertAfterErase routes expansions through HIDKeyPoster.postUnicodeInsert using synthesized Unicode key events instead of clipboard paste, ensuring complete and reliable text insertion while maintaining clipboard paste for shell/terminal environments.
  • Empty expansion guard: TextInjectionPipeline.emptyExpansionDecision distinguishes between empty expansions meant solely for caret repositioning (which erase the trigger without pasting) and unintended empty results (which safely refuse the expansion, leaving the trigger intact).

AI preview: the result was never drawn

The preview panel opened, the transform ran, the delta pills updated, Replace inserted the
correct text — and the text area was empty. The result was in the text view the whole time;
there was nowhere to draw it. On the shipped build the panel's accessibility tree read:

AXScrollArea [632x277]
  AXTextArea  [0x277]  VALUE(134)="we were talking about the api design yesterday…"
  • A document view has to be told to follow its viewport. A scroll view does not size the
    view it scrolls. The panel built its result view with a bare NSTextView() — a zero frame —
    and assigned it as documentView without any of the document-view sizing that
    AlertPresenter, TestExpansionLab, PermissionDiagnosticsController and
    SnippetEditorSheet all set. Its width was therefore whatever AppKit happened to give it
    when the scroll view was first tiled, and nothing ever corrected it. In a test process that
    is the viewport width, so the text drew and the panel's own tests passed; in the shipped app
    it was zero. The text view now takes autoresizingMask = [.width], an unbounded maxSize,
    and a container that tracks it.
  • The same omission is fixed at every other site that has it. FillInPanel,
    RecoveredDictationWindowController and PermissionDiagnosticsController build their text
    views the same way; only the preview was reported, but the defect is the shape, not the case.
    A source-level contract now fails if a text view is handed to a scroll view without being
    told to follow it.

AI preview: replacing a request that is still running

Changing the tone, changing the transform, pressing Retry, or opening a second preview while the first was still generating left the preview panel blank — no result, no spinner, Replace and Copy disabled, and "Another AI transform is already running. Wait for it to finish." pinned underneath with no way to clear it. The request the user had just asked for never ran. Three defects combined to produce it, and each is fixed at the layer that owns it.

  • Abandoning a transform now stops it. AITransformDiscardHandle.discard() dropped the result and left the work running — "the model may keep running" was the documented contract. A running generation holds AITextTransformer's single-flight latch, so the replacement request was refused .busy by the request it had just replaced. discard() now cancels the generation as well, runTransform checks for cancellation at every stream snapshot, every chunk and every re-roll, and the new settled() reports when the work has actually unwound.
  • A replacement waits for its predecessor instead of racing it. transformStreaming(after:) queues a request behind the one it replaces, so cancellation being cooperative — and therefore not instantaneous — cannot turn a handover into a .busy refusal. The preview panel passes the abandoned request across panel boundaries too, so opening a second preview mid-generation is a handover rather than a collision.
  • A superseded request can no longer touch the panel. isCurrent() only answered "is this panel still open", which cannot distinguish one of the panel's own requests from another. Every partial and completion now carries the generation it belongs to and stale ones are refused before anything is drawn. Previously the replaced stream kept writing its partials into the text view under the new transform's title, and its .discarded notice tore down the spinner and discard handle belonging to the request that had replaced it.
  • Cancelling is no longer recorded as a model failure. A cancelled generation was classified as .unknown, logged, and written to AIDiagnosticsStore, so every mid-stream tone change counted against the model's measured reliability. It is now classified as .discarded.

Four adjacent defects in the same panel are fixed alongside it:

  • The panel is the size it declares. panelSize said 560pt wide while the header laid the panel out at 652pt in every shipped language, so FloatingPanelChrome.positionNearTop centred a width the panel never had (46pt off-centre) and the error label declared a wrap width 92pt narrower than the one it was given. The declared size is now 660×460, the content view takes a required upper bound at that size, and the two free-form header labels truncate rather than push the panel wider. The equal-size lock the sheets take is still unavailable here: it would fight the fr...
Read more

DevType v1.1.0

Choose a tag to compare

@github-actions github-actions released this 13 Sep 18:23

DevType 1.1.0 — Secrets, and undo you can turn off

The GitHub artifact follows the project's existing ad-hoc signed, unnotarized distribution policy. Gatekeeper may reject it. Local packages use the available Apple Development identity. See the audits linked below for verified checks and open gates.

Secrets

The Secrets manager was a mouse-only window with a list that did not match the rest of the app. It now shares the snippet manager's header and glass-card list, with two-line rows carrying tags and a disabled badge, and separate empty states for "none yet" and "none matched your search" — the second no longer offers to create a secret you did not ask for.

  • Full keyboard operation. Return copies the selected secret and Delete removes it. Both are bound to the list rather than to the buttons, so Delete cannot fire while you are typing in the search field. Every action in this window previously required a mouse.
  • Show/Hide in the secret editor. A typo in a secret used to surface only when a paste later failed, and the usual fix was to retype it blind. The field is still concealed by default, a stored value is still never prefilled, and revealing is explicit and per-sheet — the editor says so on screen while it is in effect. This is a deliberate, narrowed trade against shoulder-surfing; see SECRETS.md for the guarantee as it now stands.
  • Copy Secret submenu caps at 20 and names how many are not listed, instead of presenting a capped sample as the whole set. Disabled secrets are excluded.
  • Search Secrets has its own empty text rather than borrowing the snippet palette's.

Underneath, the secret/snippet boundary is now enforced by types rather than by guards a caller could get wrong. Secrets cannot reach the snippet list, search indexes, exports, prompts, or diagnostic messages — not because each path checks, but because the states that would allow it are no longer representable.

  • Disabled secrets no longer appear in the copy menu, and a selected record is revalidated before and after authentication, so a secret deleted, disabled, or edited mid-prompt cannot still be read.
  • Two editors open on the same secret can no longer interleave and overwrite the winning value; a retried rollback cannot overwrite a newer committed one.
  • Cleanup no longer deletes a secret that a newer or unexamined library still references; uncertain items are retained and reported separately.
  • A secret matching an AI snippet-tool query no longer consumes the single result slot and returns an empty trigger.
  • Malformed or ambiguous library metadata is refused instead of becoming an empty usable library that then overwrites the real one.

Values never enter library JSON, snippet exports, search indexes, or prompts. Legacy identifiers, archive encryption, master-key preservation, and clipboard ownership and expiry are unchanged. Details in the isolation audit and the boundary audit.

Backspace undo

New setting: General → Typing → "Undo expansion with Backspace." Default on, which preserves existing behavior. Turning it off stops undo being recorded or claimed, clears stale records, and revokes work already queued.

Undo now runs through the same injection lifecycle as expansion itself, inheriting target checks, cancellation, timeouts, host capability policy, and clipboard ownership rather than posting through its own parallel chain.

  • Refusals explain themselves. Every refusal used to claim the target text had changed. The messages now distinguish unreadable text after input, an active selection, a changed context, and a reversal that simply cannot be verified. Raw field text is not included.
  • Repeated text is no longer ambiguous. Typing aa and then aa again could erase three characters and restore the wrong one. Undo now anchors to the recorded insertion position; missing evidence or several unanchored matches refuse rather than guess.
  • Erase geometry respects character boundaries. Slices inside surrogate pairs, combining marks, and joined emoji are refused before a lossy decode can become false evidence.
  • Widening is capped at 16 UTF-16 units and freshness at five seconds; zero, negative, and arbitrarily large limits no longer bypass the advertised caps.
  • An input-counter overflow that could terminate the app is fixed — counters use saturating arithmetic.

Native application Undo, snippet-editor Undo, and "Undo last AI" remain separate features. Details in the undo audit.

Delivery

An AI result or a dictated segment is generated before it is delivered, so a delivery that refuses at the last step loses work the user already waited for. Two things made that happen more often than it should have, and made it hard to tell why.

  • A target that was still arriving read as a target that had moved. Waiting for the source app to come back to the front stopped as soon as focus left DevType — which is the moment an app republishes its focused element while the window server finishes the switch, and so the moment that element is most likely to be replaced a beat later. The delivery captured it, the insertion re-read focus, saw something different, and refused. The element now has to answer twice running before it is captured. This costs one poll interval when focus is already settled; the overall budget is unchanged, and an app that never settles still delivers at the end of it rather than dropping the payload.
  • Safari and other WebKit apps never benefited from that wait at all. Web content publishes its focused element from a separate process, so a wait keyed to the source application's process could not be satisfied there — every delivery spent the full budget and then read anyway. The wait no longer asks which process owns focus, only whether focus has left DevType and stopped moving.
  • Refusals name the cause. "Target element or selection changed" covered an app switch, the caret landing in a different field, and focus that had not been published yet — three different problems behind one sentence. The diagnostic report now carries which of them applied. Relatedly, the check that decided a refusal and the check that labelled it were two separate readings taken moments apart under different rules, so in apps where DevType trusts the accessibility caret position (Notes, Xcode) a refusal genuinely caused by the caret moving re-tested clean and was filed as unreproducible. One reading now does both.
  • A suspension held during an AI delivery reported itself as belonging to the secrets subsystem in diagnostics, having been named after the code it was adapted from.
  • An AI action invoked with DevType in front no longer runs the model first and then throws the result away. The action panel captures whatever is frontmost as the app to deliver into, and delivery refuses to insert into DevType's own window — so using the palette while a DevType window had focus read the selection, ran the transform, and refused at the last step, reporting that the source application "did not return to the front" about an application that had never left. That case is now refused the instant the shortcut fires, before any work is spent, and says what is actually true: DevType is in front, switch to the app you want to insert into.
  • The four ways a panel delivery can have no target are now four sentences, not one. No source application captured, DevType itself, the source having quit, a third application taking over, and focus never arriving had all been reported identically. The diagnostic report also names which panel lost the payload — an AI result, a palette value, and an inline-search expansion had been recorded under separate paths precisely so the report could tell them apart, and then printed without it.

Security

From a review of the secrets, import, and typing paths.

  • Turning off "Require Touch ID" now requires Touch ID. It was an unauthenticated setting, so anyone at an unlocked Mac could switch the requirement off and then read every stored secret without being asked — the gate protected the values while nothing protected the gate. Switching it off now has to satisfy the requirement it is about to remove, and is asked fresh rather than accepting a check from the last thirty seconds. Cancelling or failing leaves the requirement on. Turning it on is still immediate. Menus and the Preferences switch now show what is actually in force rather than what was clicked.
  • Importing an Espanso package can no longer take the app down with it. A YAML file using a legal but unusual construct — a list as a mapping key — terminated the process mid-import, before the preview you were going to decide from. Aliases were also expanded by copying, so a few hundred bytes could ask for more memory than the machine has. Imported YAML is now admitted under explicit size and nesting limits, and shapes Espanso does not use are refused with a message instead of being attempted.
  • Macro-heavy text no longer stalls the interface. A document dense with unterminated macro markers was re-scanned from each marker to the end of the text, which at the import size limit measured around two seconds of blocking work — on a list that renders a preview per row, and an editor that renders one per keystroke. Scanning now draws from a per-document allowance far above anything ordinary text reaches.
  • Keys typed during an expansion are no longer replayed into the wrong app. Characters held while one app was frontmost were replayed wherever focus happened to be by the time the hold ended, which both leaked what was typed and corrupted the field it landed in. Replay is now refused when focus has demonstrably moved to another application; an unreadable reading on either side is not treated as evidence, since dropping keystrokes on ...
Read more

DevType v1.0.0

Choose a tag to compare

@github-actions github-actions released this 11 Sep 14:26

DevType 1.0.0 — Productivity and reliability

The GitHub artifact follows the project's existing ad-hoc signed, unnotarized distribution policy. Gatekeeper may reject it. Local packages use the available Apple Development identity. See the audit for verified checks and open device/provider gates.

Find the right snippet

  • Search exact phrases with quotes: "best regards".
  • Combine title:, trigger:, group:, tag:, and content: filters: group:"Client Work" tag:billing.
  • Exclude literal matches with -draft or -tag:personal.
  • Find snippet types with is:enabled, is:disabled, is:secret, is:image, is:ai, and is:text. The manager can find disabled entries; the insertion palette continues to omit them.
  • The manager uses one shared indexed search pass, with group-aware filters and the same matching behavior as the palette.

Search remains local. Body matching covers the first 2,000 characters of each snippet; queries accept at most 12 terms and 4,096 UTF-8 bytes. The palette also retains its 512-character input limit. Over-limit queries and incomplete filters show an explanation and return no rows; trailing exclusions are never silently dropped. Unknown prefixes remain searchable text. Field filters, quoted phrases and exclusions are literal; ordinary positive terms retain fuzzy matching.

Work with text offline

  • New snake_case, kebab-case, camelCase, and PascalCase tools in the existing command palette and macro picker.
  • Both macro syntaxes work: {{snake:HTTPServer}} and %case:camel%HTTPServer%caseend%. Cursor markers remain positioned through transformations, including acronym boundaries.
  • Naming conversion preserves non-Latin letters and accents, handles transitions after uncased scripts, and normalizes expanding capitals such as ß at word starts. It converts naming styles; it does not transliterate text or guarantee a valid identifier for every programming language.
  • Line tools recognize Windows CRLF, CR, LF and Unicode line separators and produce LF output.
  • URL Encode now escapes an individual URL component, including &, =, +, ?, #, and /.
  • JSON formatting accepts scalar JSON documents as well as arrays and objects.
  • The calculator shares the macro engine's number formatter, avoiding integer-boundary crashes and preserving small nonzero results.

Reliability

  • Search caches distinguish library owners and revisions, refresh complete snippet metadata, and use each index's locale.
  • Custom rankings reuse cached lexical matches while recalculating caller-specific boosts. Extreme boosts cannot overflow.
  • Cached palette rankings refresh generated UUIDs and date/time values.
  • Concurrent cache insertion cannot grow the eviction queue through duplicate entries. Negative result limits return no results.
  • A repeatable stress command covers search, transformations, input/undo and voice delivery: DEVTYPE_STRESS_ROUNDS=20 ./Scripts/stress-productivity.sh.

No dependency, cloud service, permission grant, secret migration, or authentication change is introduced. Existing libraries and preferences retain their formats. See the audit for measured verification and open release gates.

DevType v0.1.9

Choose a tag to compare

@github-actions github-actions released this 02 Oct 12:53

DevType v0.1.9

v0.1.9 hardens text insertion, secret storage, voice-session recovery, AI cancellation, subprocess handling, and diagnostic reporting. It includes 84 new regression tests covering failures reproduced across the September 9 audits.

Fixes

  • Secret storage: distinguish unavailable archives from missing files, retain every quarantined recovery copy, refuse unsafe file types, cap archive sizes, and report failed stale-copy eviction honestly. Successful Keychain fallback reads remain separate from failed optional consolidation probes.
  • Reliable file publication: secret archives and voice records share an atomic writer with unique, owner-only staging files. Failed writes preserve the previous destination, and voice record limits match the recovery reader.
  • Voice lifecycle: recheck session ownership after asynchronous setup and before capture/finalization. Retired audio callbacks cannot update the current HUD.
  • Safe text replacement: reject contradictory erase plans before any mutation, and use bounded exact-range Accessibility evidence when a host exposes a stale whole-field value. Missing evidence remains explicitly unverified.
  • Ordered voice delivery: serialize live edits, join pending work before final delivery, await the actual insertion outcome, and close live admission after finalization. Stale sessions and late callbacks cannot write into a replacement session or claim its delivery.
  • Bounded recognition and cancellation: validate speech revisions and metadata, cap result/session/queue retention, preserve complete accepted batch evidence, and cancel tasks outside the session lock to prevent reentrant deadlocks.
  • Input and test isolation: bound combining-mark payloads, normalize invalid capacities and hold intervals, and isolate default XCTest diagnostics from the installed app's files.
  • AI deadlines: palette routing and Apple Intelligence correction return after a bounded cancellation grace. Unfinished operations retain their admission slot so repeated requests cannot accumulate model work.
  • Process and numeric bounds: cap diagnostic tool output, close inherited-pipe waits without stranded reader tasks, reject incomplete identity probes, and validate nonfinite or overflowing budget/deadline inputs.
  • Accurate diagnostics: unverified text posts no longer count as verified delivery. Ordinary typeahead safeguards are separate from suspected duplicate writes. Repeated Accessibility probes are coalesced and backed off.

Validation

  • Final full local CI: 2,908 tests executed, one skipped, zero failures; all five optional benchmarks ran.
  • Final delivery/session Thread Sanitizer run: 81 tests passed without sanitizer findings. The earlier storage/platform audit also passed its 92-test run.
  • Final delivery/input Address Sanitizer run: 243 tests passed without sanitizer findings. The earlier storage/platform audit also passed its 59-test run.
  • Debug/release builds, packaging, strict code-signature verification, and required weak Foundation Models linkage passed during the audit.
  • The skipped test requires a local whisper.cpp server and model. Live Keychain authorization, physical microphone/device changes, and third-party app interactions remain separate verification gates.

The evidence and limits are recorded in the initial hardening audit, diagnostic follow-up, and delivery/session audit. Local installation uses the existing Apple Development signing identity; notarized distribution and public release publication are separate steps.

DevType v0.1.8

Choose a tag to compare

@github-actions github-actions released this 05 Sep 18:34

DevType v0.1.8

Built on the GitHub macos-26 runner. The exact Xcode, build and SDK are stamped into the bundle (DTXcode, DTXcodeBuild, DTSDKName, DTPlatformVersion) and printed in Diagnostics, so the toolchain behind any given DMG is readable from the artifact itself.

v0.1.8 restores Apple Foundation Models and Apple SpeechAnalyzer capabilities to public GitHub releases, adds automated binary capability verification prior to DMG packaging, and stamps build toolchain metadata into the application bundle and diagnostic reports.

Foundation Models & SpeechAnalyzer restored

  • macOS 26 CI runner migration: The release workflow (.github/workflows/release.yml) and the packaging job (.github/workflows/ci.yml) now run on macos-26 runners, whose Xcode 26 toolchain has the Foundation Models SDK. This resolves the defect where v0.1.7's public DMG was built on macos-14 / Xcode 15.4, compiling every #if canImport(FoundationModels) and #if compiler(>=6.0) guard to its fallback: the download had no AI transforms, no Local AI dictation corrector, no semantic palette routing and no macOS 26 speech engine, and told macOS 26 users their OS was too old.
  • Accurate unavailability messaging: A Mac that could run the models, held back by the binary it was given, now says so. AIModelAvailability.Reason.buildLacksFoundationModels and FailureCode.buildLacksSpeechAnalyzer carry English, Korean and Japanese copy pointing at the current release. The split is drawn on the OS first: a Mac below macOS 26 keeps hearing "Requires macOS 26 or later" whatever toolchain built its copy, because a newer download cannot help it.

Release capability verification

  • Preflight Mach-O inspection: Introduced Scripts/verify-release-capabilities.sh, run by Scripts/release.sh after packaging and before the DMG is assembled. DEVTYPE_REQUIRE_FOUNDATION_MODELS=1 is forced whenever a release tag is present and set by the release workflow; without it the script skips and says so, so packaging on an older Xcode is possible but never silently unverified.
  • Weak linkage assertion: otool -L must show FoundationModels.framework present and weak. DevType deploys to macOS 14 while the framework starts at macOS 26, so a strong link would make dyld refuse to launch the app on every Mac below 26 — invisible to the machine that built it, and worse than the defect this release fixes.
  • Capability test harness: Scripts/test-release-capabilities.sh covers eight cases — gate unset skips with a printed notice, missing path, missing bundle, missing binary, weak linkage passes, absent framework fails, strong linkage fails, and a failing otool is reported as an inspection failure rather than read as "not linked". It runs from Scripts/ci-release-fixtures.sh, so ci-local.sh and the release job both execute it.

Build toolchain stamping & diagnostics

  • Info.plist metadata: Scripts/package-app.sh extracts build toolchain details via xcrun and stamps DTXcode, DTXcodeBuild, DTSDKName, and DTPlatformVersion into the packaged bundle's Info.plist.
  • Diagnostic reports: DiagnosticReport now inspects bundle toolchain metadata and emits a "Built with Xcode <xcode> · SDK <sdk>" header line in runtime diagnostic reports (⇧⌘D), making the compiler and SDK version immediately apparent in bug reports and support logs.
  • Contract enforcement: Scripts/test-package-signing-contract.sh, Scripts/ci-local.sh and .github/workflows/ci.yml all require the four toolchain keys in every packaged build, and the macos-26 packaging job now runs the linkage check on each pull request rather than only at tag time.

Snippet editor stops growing as you type

  • Fixed-size sheets hold their size: The snippet editor widened with every keystroke once the trigger rule sentence carried a conflict message (or a long trigger, or a long group name), pushing Cancel and Save off the screen. A window under Auto Layout keeps its size only at priority 500, below every label's default compression resistance, and the contentMinSize / contentMaxSize clamp shipped in v0.1.7 never entered the constraint solve. NSWindow.dtLockContentSize(_:) (Sources/DevTypeAppCore/FloatingPanelChrome.swift) pins the content view with required constraints; the snippet editor, group editor, app-scope sheet, macro palette, template picker, fill-in panel, and expansion lab all take it.
  • Labels truncate where they sit: The trigger rule and status readouts, the group popup, the character count, the attachment file name, the fill-in form title and field captions, and the AI preview error now bound their own width, and the wrapping labels in the new-snippet guide, group editor, and app-scope sheet declare the width they will be given so a second line is laid out rather than clipped.
  • Coverage: Tests/DevTypeAppTests/SnippetEditorPanelSizingTests.swift drives the real panels through their field editors (crowded-library conflicts in every shipped language, 300-character triggers, 5,000-character bodies, long group names, sheet presentation, Escape to close) and measures the frame; SourceContractTests requires every fixed-size panel to take the lock.

Paused is reported as Paused

  • "Tap Failed" no longer means "you paused it": a paused engine has no event tap by design — PermissionCoordinator.applyTapLifecycle only starts one when the engine is enabled. EngineDisplayStatus.resolve checked isTapRunning before isEnabled, so a one-click pause was reported as Tap Failed, whose recovery copy points at TCC identity mismatches and duplicate processes. Users could reinstall, reset permissions and re-approve every prompt without touching the flag that actually mattered. Only a tap that was asked to run can have failed, so the isEnabled guard now sits on that branch; Secure Input keeps its precedence over pause, and genuine tap failures are unchanged.
  • Why no test caught it: every prior test of the paused branch passed isTapRunning: true alongside isEnabled: false, which the running app cannot produce. SecureInputPresentationTests was worse — its expected value restated resolve's branch order verbatim, so it agreed with the implementation by construction. Its expectation is now written as rules, which is what makes the exhaustive sweep mean anything.

A secret-storage repair you can actually reach

  • Repair Secret Storage (Preferences ▸ Advanced ▸ Maintenance) runs the app's one interactive keychain pass on demand, and says up front how many system dialogs to expect.
  • The gap it closes: keychain ACLs are pinned to the signing identity, so a rebuild or a switch between an ad-hoc DMG and a locally signed build leaves items readable only after a one-time "Always Allow". migrateLegacy already repaired every such account — including the consolidated store's master key — but its only caller gated on snippetIDsPendingMigration(), which maps accounts to snippet UUIDs and silently drops the master key. When the master key was the only casualty, the app re-read it every launch, failed, reported "present but UNREADABLE", and offered nothing. That is the same shape as the §8.10 v2 items whose silent read "knew a dialog would fix it and nothing in the app could ever show one", one layer up.
  • No secrets were ever at risk: the store refuses to mint over a master key it cannot read, because those bytes are the only way back into every sealed entry, and falls back to per-item keychain storage, which loses nothing. This release restores the path back to consolidated storage rather than recovering data.
  • Security impact: no widened access and no new storage path. It makes an existing repair user-invocable, so DevType can now show a keychain prompt in a situation where it previously showed none — always behind an explicit button and an alert that states the dialog count first. Automatic keychain prompts remain something the app refuses to produce.

Validation and distribution status

  • The local gate was run on Xcode 26.6 (build 17F113, SDK macosx26.5); the packaged binary weak-links FoundationModels.framework (LC_LOAD_WEAK_DYLIB), which the release job now asserts before publishing.
  • The release gate is DEVTYPE_BENCH=1 DEVTYPE_SKIP_AUTO_CERT=1 ./Scripts/ci-local.sh: engine and UI suites, shell fixtures, package signing contract, toolchain stamping, Foundation Models linkage, and installer fixtures.
  • Public release DMGs are ad-hoc signed and unnotarized; local builds support Apple Development signing.

DevType v0.1.7

Choose a tag to compare

@github-actions github-actions released this 05 Sep 06:09

DevType v0.1.7

v0.1.7 consolidates the audited expansion fixes and the runtime/UI cleanup branch into main. It focuses on delayed-action authority, clipboard integrity, recoverable library conflicts and predictable macro output.

Expansion and clipboard integrity

  • Delayed paste, modifier-gap callbacks, cursor positioning and trailing keys share one operation lifetime. Cancellation, supersession, a changed target field or app, user input and live permission checks can stop later actions. After HID erase, clipboard paste does not treat a flickering accessibility selected-range as a moved caret: Chromium/Electron hosts often report a different or nil range for tens of milliseconds while the same field stays focused. Proven native writers can still refuse before erase if the caret moved. Command is released even when a paste is cancelled during the modifier gap. Text and image paste refusals log the matching reason — cancelled or superseded work, Post Events denied, Secure Input, a changed target, lost clipboard ownership, or Cmd+V not posting — instead of reporting every refusal as a Post Events failure or only restoring the trigger.
  • Text, image and secret publication check every payload/marker write against the original clipboard ownership count. Failed writes do not schedule a paste or report a successful menu copy. Recovery replaces owned partial contents instead of appending the previous clipboard beside them; newer external copies are preserved by ownership checks. A clipboard write failure preempts an unread success toast, so replace-and-copy cannot leave “applied” on screen while the miss waits in the queue.
  • Delivery confirmation requires a relevant transition in the pinned field and range. Existing text, unrelated edits, Unicode case-fold shifts and inconclusive deadlines remain unverified. Historical trust and repeated misses cannot authorize automatic paste or corrective-text replay.
  • Clipboard residency uses monotonic deadlines. Invalid copy timing is refused before posting, and a finite poll budget bounds copy capture even if its injected clock stalls.

Recoverable library conflicts

  • The store validates the selected library and read-back verifies recovery copies and a phase journal before adoption. Captured alternate versions are removed only after the adopted bytes are verified.
  • Adoption failed, adopted, and adopted with cleanup pending are distinct outcomes. Failed cleanup remains retryable; recovery folders are retained. The UI performs this disk work off the main thread and surfaces the outcome and recovery location.

Predictable macro and voice rendering

  • An explicit operation context pins source/dependencies, date, clipboard and occurrence-specific UUID/random/counter values across preparation and fill-in delays. Separate quick expansions remain separate operations. Counter mutations persist in order without blocking preference callbacks that only read values; cancellation can leave reserved counter gaps.
  • Both syntaxes carry structured cursor anchors through substitutions and Unicode case transforms. The first final cursor position wins. Clipboard, fill-in and generated values remain literal data, preserving template-shaped text without executing or stripping it.
  • Rendering has typed size/work/structure failures and never sends a partial payload or retained trailing action on failure. The expansion lab passes its prepared result into injection rather than evaluating volatile macros twice.
  • Transcript comparison trims common text, bounds input/token/matrix work and observes cancellation. If a comparison is omitted, the HUD retains complete cleaned text without claiming a complete diff.

Runtime and UI consolidation

  • Shared owners now handle usage-sidecar persistence, support-directory resolution, file modes, saturating arithmetic, single-flight AI operations and local-provider readiness checks.
  • Floating panels, hover tracking and palette row selection share their existing behavior through common components. Dead paths and duplicate implementations were removed on the cleanup branch and merged with the current audit fixes.
  • Search, prefix matching, nested lookup, localization caching, library interaction and thumbnail improvements from the audited working tree are included.
  • When the engine needs attention, the status-item ⚠ is shown on Diagnostics (⇧⌘D). Permission Recovery keeps its localized title so the warning does not point at the wrong menu command.

Validation and distribution status

The release gate is DEVTYPE_BENCH=1 DEVTYPE_SKIP_AUTO_CERT=1 ./Scripts/ci-local.sh. A local run on commit 48eb01b (Swift 6.3.3 / Xcode 26.6) executed 2,797 Swift tests with 0 failures, plus installer 15/15, signing/DMG/asset/preflight/version fixtures, and codesign --verify --strict.

GitHub publication waits for reusable macOS 14/26 CI, then runs Scripts/ci-release-fixtures.sh (19 publication tests) before building the untrusted DMG. It does not re-run the engine suite after that CI has passed; a green CI job plus a flaky ci-local.sh rerun failed a v0.1.6 publish. Release notes must end with a newline so the GitHub body round-trip stays byte-identical.

Regression coverage includes failure injection, cancelled/superseded actions, Unicode and mixed macros, concurrent persistence, isolated native pasteboards, temporary file versions and reopening adopted stores.

Physical cross-application focus/Secure Input races and live iCloud conflict/process-restart scenarios remain manual verification limits. Shared clipboard and global keyboard APIs do not provide an atomic cross-process transaction. Clipboard snapshots remain bounded to eight items/four MiB and exclude promised-file representations; retained recovery folders are not automatically pruned.

The packager stamps CFBundleShortVersionString: 0.1.7 and derives the build number from Git commit count. Local signing is Apple Development: (SV7KQ26548) and is not notarized. Strict codesign and preserved TCC identity do not imply Gatekeeper acceptance on other Macs. The GitHub artifact is the same untrusted, unnotarized DMG. The canonical installation target is /Applications/DevType.app.

DevType v0.1.6

Choose a tag to compare

@github-actions github-actions released this 04 Sep 20:44

DevType v0.1.6

DevType v0.1.6 is a major capabilities, localization, and safety release. It introduces
configurable multi-mode Smart Dictation delivery with non-destructive replacement guards,
loopback network transport security, full native tri-lingual localization across English, Korean,
and Japanese, interactive snippet conflict resolution with atomic edit transactions, an
interactive permission recovery workflow, and atomic installation with automated stale-build quarantine.

Hot patch: scrollable update details

  • Long release notes remain usable: Check for Updates… now keeps release notes inside a
    bounded, vertically scrollable region instead of allowing the modal to grow beyond the screen.
  • Always-reachable dismissal: A localized Close button stays above the release notes and
    supports the Escape key. View Release, Skip This Version, and Later remain visible
    below the scrolling content.
  • Empty-note and localization coverage: Releases without a notes body show an explicit empty
    state, with matching English, Korean, and Japanese copy.

Smart Dictation pipeline redesign & multi-mode delivery

  • Configurable "While you speak" delivery modes: Dictation delivery and recognition are now
    cleanly decoupled into three user-selectable modes in Preferences → Voice:
    • Type into the document as I speak (typeAsYouSpeak, default): Recognized words are typed
      progressively under the caret and atomically reconciled against the final, proofread transcript
      at session close.
    • Show words in the bubble, insert at the end (showWordsInBubble): The floating Liquid Glass
      HUD displays the live stream while leaving the host document completely untouched; the
      polished transcript is inserted in a single atomic transaction when dictation ends.
    • Show nothing, insert at the end (showNothing): Non-intrusive listening mode with no live
      partial transcription preview. It delivers the finished text upon session completion and does
      not require the macOS Speech Recognition authorization grant.
  • Non-destructive replacement guard (VoiceDestructiveReplacementGuard): Dictation replacement
    now evaluates a strict deletion ceiling (maxDeletionRatio). If a finalized transcript accounts
    for materially less than the text already visibly typed or dictated on screen, replacement is
    refused and existing content is preserved.
  • Loopback transport security & ATS enforcement: Local speech and correction endpoints (Local
    Whisper / whisper.cpp, Ollama, and OpenAI-compatible servers) are strictly pinned to loopback
    hosts (127.0.0.1, localhost) with redirect fan-out prevention and per-operation response size
    limits (LocalEndpointSecurity, LocalCorrectionEndpointRoute). Loopback networking is declared
    narrowly via NSAppTransportSecurity -> NSAllowsLocalNetworking in Info.plist without lowering ATS
    defenses for external traffic.
  • Two-stage explicit consent for cloud audio: The opt-in Gemini 3.5 Transcribe engine requires
    both a user-provided API key stored securely in the login Keychain and a distinct, explicit
    cloud-audio consent checkbox in Preferences. Missing prerequisites fail closed before audio
    capture begins rather than silently altering routes or fallback providers.
  • Audio pipeline resilience & crash journaling: Captures handle AVAudioEngineConfigurationChange
    events gracefully without thread deadlocks or leaked audio taps during Bluetooth/headphone handoffs.
    Audio is streamed to millisecond-1 journals (capture.caf in
    ~/Library/Application Support/DevType/VoiceSessions/). If a session terminates abnormally, the
    new Recovered Dictation window controller (RecoveredDictationWindowController) enables users
    to review, playback, and transcribe orphaned audio.
  • Dynamic session watchdog: Each recording session is guarded by a dynamic, snapshot-derived
    timeout (SessionWatchdog) preventing stalled recognition engines from freezing dictation states.
  • Terminal voice diagnostics: A real-time diagnostic panel in Preferences
    (VoiceTerminalDiagnostic) exposes live provider readiness, audio sample statistics, latency
    measurements, and operational traces.

Full tri-lingual localization

  • Shipped string tables: Complete, native localization in English (en), Korean (ko, 한국어),
    and Japanese (ja, 日本語) across all shipped bundles (Resources/en.lproj, Resources/ko.lproj,
    Resources/ja.lproj).
  • Universal copy parity: Covers InfoPlist.strings, system permission explanations, preference
    panes, conflict resolvers, onboarding flows, recovery prompts, and error dialogs.
  • Dynamic locale switching: Users can switch interface language live in Preferences → General;
    LocalizationManager refreshes all open windows, sheets, and menus instantly without requiring an
    application restart.

Snippet management, atomic transactions & conflict resolution

  • Interactive Conflict Resolver (SnippetConflictResolverSheet, ConflictResolverSnapshot):
    Detects trigger collisions and ambiguities across standard and regex snippets, providing
    side-by-side visual diffs and atomic resolution options.
  • Atomic edit transactions (SnippetEditTransaction): Eliminates state race conditions during
    concurrent snippet mutation, bulk tag assignment, import staging, and duplication.
  • Recent Snippet Resolver (RecentSnippetResolver): Fast LRU resolution and prefix lookups
    optimized for high-frequency keystroke matching.
  • Bounded image attachment store: Imposes storage quotas and automated orphaned asset pruning on
    rich snippet attachments (ImageAttachmentStoreLimitTests).
  • Hardened secret snippets: Biometric Touch ID gating uses fresh evaluation contexts per request,
    protects master-key access-control lists, and safely isolates clipboard restoration from write
    failures (SecretClipboardWriteFailureTests).
  • Lifetime Unused Snippet Insights & Navigation: Statistics actionable insights accurately surface
    snippets with zero lifetime usage (rather than period-scoped single-use). The "Review Unused" action
    opens the Snippet Manager filtered directly to .unused across the entire library without inheriting
    stale local group or search scope (StatsPeriodPresentationTests).

Permission architecture & system recovery

  • Partitioned permission model: Strict separation between Input Monitoring, Accessibility, Post
    Events, Microphone, and Speech Recognition.
  • Permission Recovery Controller (PermissionRecoveryController): Interactive troubleshooting
    sheet guiding users through missing grants with direct deep links to macOS System Settings panes.
  • Hardened TCC reset tooling (Scripts/reset-tcc.sh): Validates and resets TCC records across all
    subsystems with non-zero exit codes on failure.

Packaging, signing & atomic installation

  • Atomic installer (Scripts/install-app.sh): Validates code signature integrity, bundle
    identifiers, and designated requirements before replacing the destination (/Applications/DevType.app
    with fallback to ~/Applications/DevType.app). Keeps the prior bundle available for immediate
    rollback until post-move validation succeeds.
  • Automated stale-build quarantine: Quarantines displaced binaries, source artifacts, and duplicate
    stale bundles into unique, timestamped paths under build/.quarantine/ to prevent Launchpad and TCC
    identity confusion.
  • Strict signing contract verification (Scripts/package-signing-contract.sh, Scripts/test-package-signing-contract.sh):
    Verifies that staged bundles preserve CDHash stability across incremental builds and adheres strictly to
    entitlement constraints.
  • Hardened publication resilience (Scripts/publish-release.sh, Scripts/test-release-publication.py):
    Protects GitHub release deployment against transient network dropouts with bounded exponential backoff
    retries on remote tag resolution, asset upload, metadata inspection, and undrafting.

Verification

  • ./Scripts/test.sh: 2,604 tests executed; 2,604 passed, 0 failures.
  • ./Scripts/ci-local.sh: All validation phases passed cleanly:
    • Shell script syntax and linting checks.
    • Property list validation (plutil -lint across Info.plist, entitlements, and all .lproj strings).
    • Signing identity resolution and package signing contract verification.
    • Recoverable installer contract tests and canonical path cleanup tests.
    • Distribution signing gate, DMG selection, asset inventory, tagged release trust boundary, and draft publication tests.
    • Full SwiftPM test execution.
    • Clean Swift release compilation and bundle packaging.
    • Post-package bundle identity and version stamping verification.

Distribution status

The GitHub DMG is ad-hoc signed and unnotarized under the repository's explicit
untrusted-release policy. Gatekeeper may reject it; a valid code signature does not establish
notarization or trusted distribution. Local builds utilize the available Apple Development
signing identity (Apple Development: bharath.vbcr@gmail.com (SV7KQ26548)) and are also unnotarized.
The packager stamps CFBundleShortVersionString: 0.1.6 and derives the build number from Git commit count.
The canonical installation target is /Applications/DevType.app.

DevType v0.1.5

Choose a tag to compare

@bharathvbcr bharathvbcr released this 03 Sep 19:25

DevType v0.1.5

DevType v0.1.5 is a correctness and durability release. It closes a dictation defect that could
delete text the user had already typed, removes a path that could permanently destroy stored
secrets, gives applications a way to earn back accessibility trust they had lost forever, and
bounds a wait that could hang the app.

Verified dictation erases

  • Erases now name what they delete. Live dictation removed text by posting a bare backspace
    count. A count-only erase plan carries no expected text, and the erase precondition treats a
    missing expectation as "cannot verify, proceed best-effort" — so every dictation erase ran with
    no check on what it was removing, while ordinary snippet expansion had used a verified plan all
    along. The reconciler now names the exact text it intends to remove, so the precondition can
    refuse a mismatch instead of backspacing blind.
  • The reported failure. A count is only safe while the caret is where dictation left it. With
    existing text in the field and a caret moved between segments — by the user clicking, or by the
    host application — those backspaces landed on the user's own content.
  • Dictation no longer claims a caret guarantee it cannot make. Snippet expansion may vouch that
    the caret sits immediately after the trigger, because the event tap just watched those keystrokes
    land. Dictation segments arrive seconds apart, so it passes eraseCaretVouched: false to both
    erase backstops. Vouching reopens the defect; a test asserts exactly that.
  • Correct unit systems. A count-only plan reused a grapheme count as the UTF-16 selection width,
    under-selecting on dictated emoji and other astral-plane text. The plan now derives both counts
    from the text itself.

Dictation survives a moved caret

  • A refused edit no longer kills the session. Restoring the tracked tail after a refusal left the
    reconciler diffing against text it could no longer reach, so every later edit was refused too and
    dictation silently stopped for the remainder of the session. The repair now restores the tail and
    seals the commit boundary in one operation, retiring the unreachable text so the next segment is a
    clean append at the current caret.
  • Live typing honours the dictation target. Final delivery has always refused to write into an
    application other than the one the session was started against; live typing had no such gate. A
    segment arriving after an application switch is now withheld rather than revising a different
    document.

Secret storage durability

  • An unreadable master key is never overwritten. A keychain write can succeed against an item
    this identity cannot read, and the read-back guard correctly refused to trust such a key — but
    creation had already written over the existing one. Those bytes are the only way back into every
    sealed secret, including after the user answers "Always Allow" and the access-control list heals.
    Creation now refuses when a metadata-only query says an item is already present.
  • Consolidation reports deferral as deferral. A pass with no usable master key defers every
    secret, which is a lossless fallback the summary already defined as remaining. Reporting it as
    failed put a false alarm at the top of diagnostic reports. Settling the key once before the loop
    also stops each secret from re-attempting key creation.
  • Keychain reads report their cause. A read that needs the system dialog now carries the status
    the keychain actually returned instead of a synthesized errSecInteractionNotAllowed. Reporting
    the policy over the cause made a re-partitioned access-control list indistinguishable from a
    locked keychain in the one line a diagnostic report prints.

Accessibility trust recovery

  • A condemned application can earn trust back. A falseSuccess verdict — recorded when an
    application reports a successful accessibility write that does not change the field — was
    permanent, and its documented recovery path was unreachable: a condemned application is never
    given a write to verify, so the rehabilitation counter could never start. Verdicts now carry the
    application's build, and a build change retires the verdict for a single re-test.
  • Bounded by design. A verdict with no recorded build is never retired, so older state keeps its
    existing behaviour. Retirement also clears delivery-read proof, so a new build is not left
    half-trusted. The re-test is single-shot: an application with a prior condemnation does not get
    the two-strike allowance that exists to protect first-contact applications from one transient
    failure.
  • The focus-query retry buys information. The retry after an accessibility timeout re-used the
    same 50 ms budget that had just expired. It now escalates to 150 ms, paid only on the path that
    was about to refuse the expansion outright. A refusal also records whether the application was
    answering accessibility at all, separating "the application is hung" from "our budget was too
    small" — two causes with different fixes that previously read identically.

Stability

  • Bounded mute persistence. Muting an application waited on an untimed semaphore from a path
    reachable on the main thread. One stalled write — a full disk, a network home directory — was an
    indefinitely hung application with nothing in the log. The wait is bounded and logs a degradation;
    the mute still takes effect and observers still fire, with only the on-disk copy late.

Verification

  • ./Scripts/test.sh: 2,041 tests executed; 2,027 passed, 14 skipped, 0 failures.
  • Skipped tests require explicit opt-in for live AI Foundation Models, locally installed
    speech binaries, or synthetic AppKit window smoke tests.
  • New coverage includes a model-based dictation harness that drives a document with pre-existing
    text and a hostile caret through the real erase precondition, secret-store stress across
    hostile interleavings of save, read, consolidate and relaunch, and adversarial verdict-store
    files exercising namespace collisions and concurrent access.
  • Fourteen guards were individually disrupted and the suite re-run against each; every disruption
    was detected. Two initially escaped detection, and both were corrected by moving policy out of
    an untestable singleton into pure, directly tested rules.
  • Release configuration compiles and packages cleanly.
  • Physical editor delivery, live AI output quality, and Touch ID interaction require separate
    manual validation.

Distribution status

The GitHub DMG is ad-hoc signed and unnotarized under the repository's explicit
untrusted-release policy. Gatekeeper may reject it; a valid code signature does not establish
notarization or trusted distribution. Local builds can use the available Apple Development
signing identity and are also unnotarized. The packager stamps
CFBundleShortVersionString: 0.1.5 and derives the build number from Git commit count. The
installation target is /Applications/DevType.app.

DevType v0.1.3

Choose a tag to compare

@github-actions github-actions released this 02 Sep 02:03

DevType v0.1.3

DevType v0.1.3 makes the snippet library easier to organize and safer to use,
adds privacy-bounded suggestions for phrases you retype, and hardens voice,
calculation, and application-scoping behavior.

Library organization and customization

  • Added optional on-device tag suggestions for snippets. Suggestions are
    normalized, deduplicated, bounded, and offered for explicit acceptance;
    they never silently change a snippet or rename a group.
  • Tags are now searchable and editable directly in the snippet editor. Tag
    changes update the search index immediately and preserve import/export
    compatibility.
  • Added per-snippet and per-group application scoping with include and exclude
    lists. The picker resolves bundle identifiers from selected applications,
    preserves both lists during editing, and applies group scope consistently
    with snippet scope.
  • Disabled groups now stay disabled on the expansion path instead of remaining
    active behind a search/export-only toggle.
  • Added free-form SF Symbol and color controls for groups, including safe color
    conversion for non-sRGB picker values and rejection of unavailable symbols.
  • Added clearer filtering, tag chips, scope controls, and localized copy across
    the library and editor surfaces.

Privacy-bounded repetition suggestions

  • DevType can offer to turn a phrase you repeatedly type into a snippet, or
    remind you of an existing trigger instead of creating a duplicate.
  • The feature is off by default and requires explicit, versioned consent.
  • Detection reuses the existing bounded keystroke buffer, skips Secure Input
    and muted applications, and refuses long unbroken or mostly numeric input.
  • Before the threshold is reached, the detector stores only a salted in-memory
    digest and count—not the phrase. No repetition data is written to disk, and
    Forget rotates the salt and clears the detector.
  • Secrets, disabled snippets, and snippets in disabled groups are excluded from
    lookup. Suggestions remain offers until the user accepts them.

Voice and system reliability

  • Voice capture now re-checks session generation after asynchronous setup and
    tears down a microphone that loses the race to cancellation.
  • Ordinary task cancellation is no longer reported as a missing or broken
    microphone.
  • Added injectable voice-capture and tagging seams so asynchronous race,
    single-flight, secret-redaction, and failure paths are tested against real
    behavior rather than source-shape assumptions.
  • Moved the legacy keychain calls that cannot use the data-protection keychain
    behind narrowly scoped Objective-C deprecation shims without changing their
    storage or access behavior.
  • Split the AppKit surface into a testable core target while keeping the
    shipped product name, executable name, bundle identifier, and packaging path
    unchanged.

Safer transformations and faster interaction

  • Fixed calculator overflow at the positive Int64 boundary, unary-minus
    precedence, parenthesized negative expressions, negative exponents, and
    floating-point result formatting.
  • Relative-date command-palette patterns are compiled once instead of being
    rebuilt for every keystroke.
  • saveSnippets now returns its existing SaveOutcome, allowing callers to
    react to a failed persistence operation without changing current call sites.
  • Added app-target localization coverage so UI-referenced keys cannot silently
    fall back to raw key names in English, Korean, or Japanese.

Command palette and library workflow

  • Conversational palette ranking now tolerates filler words while retaining capability-aware
    constraints, so natural-language searches can find useful commands without broad false matches.
  • Optional on-device semantic routing is wired end to end behind Preferences → AI → Semantic
    Search Routing
    . When Apple Foundation Models is available, a paused query can resolve through
    DevType's date, text-operation, or snippet tools; offline results remain immediate and routing
    only adds a bounded, stale-query-checked row.
  • Ephemeral math, date, and routed rows are excluded from usage statistics, so per-keystroke query
    IDs cannot grow the stats store without ever ranking a real catalogue entry. Macro palette
    ranking also uses the shared bounded usage model.
  • Tags are included in snippet search, and Preferences → Snippets can move the library to a chosen
    folder, adopt an existing JSON library, or stop syncing and return to the local store. Existing
    content is backed up and write failures leave the active library unchanged.
  • Export now includes an Espanso match/ directory option, with one YAML file per group staged
    and replaced atomically.

Diagnostics and maintenance

  • Diagnostic reports include learned Accessibility write verdicts, and AI output-mode overrides can
    be cleared with Restore Defaults.
  • The dead-code sweep wired eight previously unreachable product paths, retired twelve redundant
    declarations, and records the audit method in docs/UNWIRED_INVENTORY.md.
  • Updated the README, user guide, architecture guide, developer guide, and liveness inventory to
    describe the current palette, export, library-location, diagnostics, and test workflows.

Verification

  • Added focused coverage for repetition detection and consent, tag suggestion
    boundaries, tag search and editing, application scope composition, disabled
    groups, group customization, voice-capture races, calculator boundaries, and
    command-palette parsing, conversational routing, library relocation, Espanso
    folder export, diagnostics, and default-reset paths.
  • ./Scripts/test.sh executed 1,910 tests with 7 opt-in live-AI tests skipped
    and 0 failures on the current source tree.
  • Release packaging, signing, and physical application validation remain
    required before tagging and publishing this version. The release workflow
    runs its own ci:local gate, checks exact tag-to-bundle version agreement,
    and publishes only through the explicit untrusted-release policy documented
    below.

Distribution status

This v0.1.3 artifact is development-signed and unnotarized because Developer ID
and Apple notarization credentials are unavailable. macOS Gatekeeper may reject
the downloaded DMG. If you trust the source, use the Finder context menu
Open or System Settings → Privacy & Security → Open Anyway after the
first launch attempt. This release does not provide the trust guarantees of a
notarized Developer ID distribution.

DevType v0.1.2

Choose a tag to compare

@github-actions github-actions released this 30 Aug 00:33

DevType v0.1.2

DevType v0.1.2 adds privacy-conscious update awareness, makes AI text transforms more predictable,
and introduces a fully local Remove Markdown action that works without an available language model.

Update awareness

  • Added Check for Updates… to the app menu and Preferences.
  • Automatic checks remain off by default and run at most once per day after explicit opt-in.
  • Update checks use a bounded ephemeral session against DevType's public GitHub Releases endpoint;
    they do not send an installation identifier, download an installer, or modify the application.
  • Development builds are ordered correctly relative to their nearest tag, preventing false
    downgrade prompts for maintainers running commits ahead of a release.
  • Failed, offline, rate-limited, malformed, and oversized checks remain distinct from a verified
    “up to date” result.
  • Release navigation now accepts only strict vMAJOR.MINOR.PATCH tags and always opens a locally
    constructed URL for this repository, rejecting foreign hosts and path-traversal payloads.

Markdown and AI transforms

  • Added a first-class Remove Markdown action that runs locally on every supported macOS version
    and remains available when Apple Intelligence is unavailable.
  • Added a complementary Convert to Markdown action while preventing automatic cleanup from
    undoing formatting the user explicitly requested.
  • Model-generated Markdown can be removed without stripping Markdown that was already present in
    the user's input; code, URLs, identifiers, whitespace, and protected voice spans remain bounded.
  • Custom instructions now travel with the selected action through the palette, preview, typed
    snippet, and transformation paths instead of reaching the model as an empty custom request.
  • The AI action palette exposes locally runnable actions on machines without a supported model and
    correctly treats digits and Return as instruction-field input while that field is being edited.
  • Voice correction and Gemini transcription share the same Markdown sanitation policy, reducing
    formatting drift between typed and spoken transformations.

Documentation and contributor experience

  • Refreshed the README with current feature descriptions and screenshots for Preferences, the
    command palette, snippet editing, and the library.
  • Added a model/adapter issue template plus clearer contribution and architecture guidance.
  • Expanded voice documentation to describe the recognition and correction engines DevType
    actually ships.
  • Added a bounded script for seeding curated good-first-issue candidates.

Verification

  • Added focused version, updater, action-selection, Markdown parsing, wiring, round-trip, and
    adversarial navigation tests.
  • Release validation continues to require a clean strict tag, complete local CI, valid bundle
    identity/version stamping, and a verified application signature before installation.
  • Post-publication verification rejects missing, mismatched, stale, or unexpected GitHub release
    assets instead of accepting a matching DMG alongside residue from a failed retry.