// 001

// Contexto Uma investigação independente sobre o Logi Actions SDK, conduzida como desenvolvedor externo. O plugin está submetido ao Logi Marketplace e o código dele é público no GitHub. O próprio plugin pode ser verificado.

Tacit: Um Vocabulário Háptico para Estado ao Longo do Tempo

Design Háptico Experiência do Desenvolvedor Pesquisa de Campo Plugin Submetido

Um build, uma exportação, a execução de um agente: você aperta, e então ou fica olhando a tela ou troca de janela e esquece. O Tacit reporta o estado de uma tarefa longa pelo polegar: cinco estados semânticos, sentidos num MX Master 4 enquanto você trabalha em qualquer aplicativo que estiver na tela. O plugin é a prova; o case é a investigação que o tornou necessário: percorrer a jornada do desenvolvedor externo e registrar o que ela custa.

[ Código no GitHub → ]
Função Product Designer, dirigindo IA
Cronograma 25 Ago - 3 Set 2026
Tipo Investigação · Submetido
Stack C# · Logi Actions SDK
O MX Master 4 desenhado de perfil em traços, o atuador háptico sob o polegar
Semântico → primitivo
taskStarted sharp_state_change
taskTick subtle_collision · a cada 15 s
taskNeedsInput knock · fora do compasso
taskCompleted completed · um pulso
taskFailed angry_alert · três, a 450 ms
// O vocabulário rodando: batidas comprimidas de 15 s; o intervalo de 450 ms do failed é real

Desafio

Existe uma classe de tarefa que esses dispositivos ainda não atendem bem: as tarefas que levam tempo. A Logitech já nomeou o território: a documentação de haptics define uma categoria Progress Indicator e entrega as primitivas. O que falta acima delas é uma camada semântica: um vocabulário para estado ao longo do tempo, e uma convenção compartilhada, para que plugins escritos por autores diferentes não ensinem duas línguas a um mesmo polegar.

Abordagem

Percorrer a jornada do desenvolvedor como um desenvolvedor externo e registrar cada tropeço em estado bruto: 39 linhas até a submissão de 3 de setembro, nunca editadas, apenas acrescentadas. Matar a premissa primeiro: uma sonda de duas horas antes de duas semanas de design. Construir o vocabulário sobre pesquisa de percepção publicada, nunca sobre impressões n=1 das primitivas da própria plataforma. Testar às cegas o que uma mão consegue provar, e pré-registrar, em doze previsões datadas, o que ela não consegue.

Impacto

Um plugin funcional, submetido ao Logi Marketplace, com repositório público sob licença MIT. Dez observações de campo que um avaliador pode conferir: cinco legíveis na documentação pública em cerca de um minuto cada, cinco alcançáveis apenas construindo o plugin e tentando entregá-lo a alguém. 20/20 de discriminação às cegas no par terminal, no ajuste de intensidade Subtle.

// 01

Tarefas que levam tempo

A tese inteira, dita de saída: isto não é uma descoberta. É ler o roadmap, e construir a próxima camada sobre ele.

A execução de um agente, um build, a exportação de um vídeo, um upload grande. Você aperta, e então ou fica olhando a tela ou troca de janela e esquece. O MX Master 4 coloca um atuador háptico sob o polegar da mão que faz esse trabalho, e o Logi Actions SDK permite que qualquer plugin fale com ele.

A Logitech já nomeou este território. A documentação de haptics define uma categoria Progress Indicator; oito das quinze waveforms entregues carregam essa classificação; “um evento acontecendo em segundo plano” aparece listado como gatilho. As primitivas existem e a categoria está nomeada.

// A lacuna

O que falta acima das primitivas é uma camada semântica: um vocabulário para estado ao longo do tempo em vez de para momentos isolados, e uma convenção compartilhada, para que plugins escritos por autores diferentes não ensinem duas línguas a um mesmo polegar.

Esse enquadramento é também a primeira regra de honestidade do método. A lacuna que este case defende é de transmissão, não de conhecimento: nada aqui afirma que as primitivas da própria plataforma não foram estudadas; afirma que o que se sabe não chega ao desenvolvedor que escolhe entre dois nomes de waveform. Isso se sustenta apenas em evidência pública, e cada contagem deste case pode ser conferida na documentação pública.

// 02

Matar a premissa primeiro

Antes de duas semanas de design, duas horas para descobrir se a ideia era sequer possível.

Tudo se apoia numa premissa: um plugin precisa alcançar a mão enquanto o usuário trabalha em outro lugar. Se a entrega háptica fosse restrita ao aplicativo hospedeiro (como notificações, foco e teclas de mídia costumam ser), nenhum vocabulário para tarefas em segundo plano poderia existir. Então o primeiro build não foi o plugin. Foi uma sonda desenhada para matar o projeto a baixo custo.

Fig. 02 · o ecossistema, cada aresta observada, nenhuma inferida
Onde estava a atenção do usuário · cinco aplicativos, nenhum deles o hospedeiro
WindowsTerminal claude Figma chrome explorer
● dois deles trazem o próprio perfil Logi, e os eventos dispararam mesmo assim
Atenção acima · sensação abaixo
O ecossistema do plugin, como o SDK o descreve · e o desenvolvedor está dentro dele
Desenvolvedor externo o ator, não a plateia
Marketplace nunca percorrido
nada construído aqui estava publicado na época da sonda
registra uma fonte de eventos, HasHapticMapping declarado
Logi Plugin Service carrega aquilo para onde é apontado
um arquivo em que qualquer coisa pode escrever
Tacit DefaultEventSource.yaml · eventMapping.yaml
Três escritores uma linha de texto cada
MX Master 4 um atuador
o polegar
a mesma pessoa, nas duas faixas ao mesmo tempo um arquivo .link, escrito num diretório que ainda não existia · fricção 09 um nome semântico resolve para uma das quinze waveforms
A entrega não tem escopo o pulso chega qualquer que seja o dono da tela, e um perfil de terceiro ativo não o suprime 14 ms · seis execuções · 31 eventos
Cada aresta sólida foi vista acontecendo numa única máquina: seis execuções, trinta e um eventos iniciados pelo plugin, cinco aplicativos em primeiro plano, nenhum deles o hospedeiro. A aresta tracejada é a rota que este projeto nunca percorreu: na época da sonda, nada construído aqui estava publicado. O desvio se manteve em 14 ms sobre o intervalo nominal de 10 s da sonda. O único tropeço numerado no desenho é uma linha do log bruto de fricção, que a próxima seção abre.
// Duas horas contra duas semanas

Duas horas de sonda responderam se valia gastar duas semanas de design. Execução e entrega ambas sem escopo, e se a resposta tivesse sido não, o projeto teria morrido antes do primeiro desenho. Essa ordem, premissa antes de ofício, é a decisão mais barata deste case.

// 03

Percorrendo a jornada do desenvolvedor

A espinha dorsal do case: o plugin existe para que estas observações pudessem existir. Cada tropeço foi registrado em bruto, em ordem, nunca editado, e o log assume os nossos próprios erros na mesma tabela.

Fig. 03.1 · a jornada, desenhada como um service blueprint o que chega ao usuário fica uma linha acima do que a plataforma carrega
Instalar Configurar Iniciar Enquanto roda Precisa de você Termina Evidênciao que de fato chega ao usuário um card que não nomeia nada sobre o que reporta um único liga/desliga, dentro da preferência de intensidade um pulsosharp_state_change uma batida, a cada 15 ssubtle_collision uma batida na porta, fora do compassoknock um pulso, ou três a 450 mscompleted / angry_alert Usuárioo que a pessoa faz instala o plugin procura as configurações dele inicia uma tarefa longa troca de aplicativo, e trabalha volta para responder lê o desfecho no polegar Linha de interação Palcoo que o usuário poderia ver ou sentir acontecendo gerenciador de plugins do Options+o Tacit aparece, e não diz nada diálogo Plugin Settings: metadados do manifesto, e o idioma em que o diálogo está escrito o atuador dispara o atuador dispara na batida o atuador dispara fora da batida o atuador dispara Linha de visibilidade Bastidoreso que roda onde o usuário não vê o Logi Plugin Service carrega o plugin a partir de um arquivo .link armazenamento de configurações, criptografado e com chave, sem superfície um escritor acrescenta uma linha ao canalRaiseEvent(taskStarted) o heartbeat, sequenciado no TaskRunnerRaiseEvent(taskTick) o único estado autorizado a interromperRaiseEvent(taskNeedsInput) a figura de três pulsos é sequenciada em códigoRaiseEvent(taskCompleted | taskFailed) Linha de interação interna Suportea plataforma por baixo, e o que ela carrega ou não LogiPluginTool · PluginApi.dllMarketplace, nunca percorrido API de configurações do plugin: ActionEditor, alcançável apenas a partir de uma ação vinculada DefaultEventSource.yamlcinco nomes semânticos: nossos, não da plataforma HasHapticMappingdeclarado no LoupedeckPackage.yaml eventMapping.yamlum nome semântico para uma primitiva quinze primitivas entregues, nenhuma delas pode ser criada
E abaixo das três linhas, o ator de quem a plataforma depende · os cinco achados, na ordem da jornada:
O desenvolvedorum terceiro, fora da empresa, trabalhando apenas com o que é publicado
1O modelo de configuração presume que um plugin é um conjunto de coisas que se apertampara o usuário, isso aparece como um controle que não está lá 2A documentação silencia exatamente onde uma decisão depende deladecide se o plugin chega a ser tentado 3A ferramenta reporta um estado que não é verdadeirodecide se o desenvolvedor confia no que construiu 4O primeiro build do primeiro projeto não compiladecide se existe um plugin, para começo de conversa 5Quinze primitivas, e nenhuma forma de saber como qualquer uma delas é sentidapara o usuário, isso aparece como o padrão errado para o estado
O blueprint é a razão pela qual este case nunca argumenta por adjetivo: cada afirmação acima está numa célula, sobre uma linha, num estágio. E as células vermelhas são os dois silêncios da jornada, ambos no momento em que o usuário encontra o plugin pela primeira vez.

O método é um log de fricção com três disciplinas. As linhas são acrescentadas no momento do tropeço, não reconstruídas depois. Uma coluna quem separa dois perfis de fricção: o do designer e o da IA, porque eles tropeçam de formas diferentes e a diferença é um dado. E uma coluna veredito marca a nossa própria inexperiência como nossa: das 39 linhas até a submissão, 33 são lacunas de documentação ou plataforma, 5 são lacuna nossa, 1 fica indefinida.

Fig. 03.2 · das linhas cruas aos achados apenas acréscimo; a curadoria veio depois e em separado
39 linhas até a submissão, cruas 33 lacuna de DX5 lacuna nossa1 indefinida
5 achados de documentação legíveis em páginas públicas em cerca de um minuto cada, e os cinco produzem um único reparo
5 achados de superfície do produto alcançáveis apenas construindo o plugin e tentando entregá-lo a alguém; cinco reparos diferentes
As cinco linhas de lacuna nossa são mostradas como nossas: apresentar inexperiência como defeito alheio é o jeito mais rápido de perder um leitor que conhece a plataforma.
// A regra do corte

Um achado é um reparo, não um assunto. Os cinco achados de documentação compartilham um único reparo (a página que documenta o recurso não carrega o vocabulário que o recurso exige), então contam como cinco observações, uma correção. O segundo conjunto exige cinco.

// 04

Desenhando o vocabulário

O instrumento com que aquela caminhada foi feita, desenhado à vista de todos. Literatura antes do design: o vocabulário codifica pelo tempo, nunca pela textura, e a razão é um ranking, não uma questão de gosto.

Taxa de identificação, por eixo
ritmo93
contagem de pulsos90
amplitude70
e a amplitude é o eixo que degrada na intensidade Subtle, exatamente onde um usuário em fluxo deixa o controle
Fig. 04.1 · cinco estados, um eixo posição = tempo · altura = intensidade
started uma marca seca, no zero
progress uma batida discreta, a cada 15 s
needsInput uma batida na porta, fora do compasso
completed um pulso cheio
failed três, a 450 ms
som ao toque · ritmo, nunca o atuador vibração onde o navegador suporta
O par terminal difere em forma, não em caráter (um pulso contra três), porque é a única decisão genuinamente difícil, tomada num momento em que o usuário já espera alguma coisa. É esse par que recebe o orçamento perceptual.

As decisões carregam suas razões. O silêncio é barato e perigoso: uma execução que termina em silêncio e uma que morreu são idênticas, e é por isso que completed existe como sinal positivo. needsInput viola a cadência de propósito: todo o resto cai na batida, então o único estado que precisa de você cai fora dela. A escalada para um pedido ignorado se contrai até um platô em vez de subir indefinidamente: um canal ambiente que cresce até virar alarme mudou de categoria.

Fig. 04.2 · três fluxos, uma gramática completed · failed · needsInput
a execução, evento a evento · intervalos reais nomeados
janela de silêncio20 s reais · uma tarefa que termina aqui nunca fala
startedum pulso · anunciado depois da janela
progressa batida de 15 s · o silêncio entre batidas é informação
needsInputuma batida na porta, fora do compasso
escaladaas batidas seguintes contraem ×0,75 até um piso de 4 s · nunca desiste
resposta dadaa batida retoma
completedum pulso cheio, e o canal fecha
failedtrês a 450 ms, e então silêncio · uma figura, não um alarme
esperas comprimidas · figuras em velocidade real o som só começa com um toque
O estado que nunca desiste é o que custa mais caro ignorar: needsInput se contrai até um piso e se mantém. Os estados terminais falam uma vez e fecham: o silêncio depois de uma figura é o design, não um sinal perdido.

E um número foi encontrado na mão: o intervalo da figura é de 450 ms, não os 220 ms com que começou. A 220, o segundo e o terceiro pulsos de uma figura nunca chegavam ao atuador: o piso mecânico fica uma ordem de grandeza acima do piso perceptual de 10 ms que a literatura oferece. Nada na documentação do SDK menciona isso.

Fig. 04.3 · os dois padrões mais próximos são os dois que jamais podem ser confundidos quinze primitivas, ouvidas em sequência · 4 dos 105 pares soaram próximos
muito próximos próximos próximos muito sutis sharp_collision sharp_state_change knock damp_collision mad ringing subtle_collision completed jingle damp_state_change firework happy_alert wave angry_alert square nenhuma colisão reportada aqui
Progress indicator · 8 de 15 Precision enhancer · 4 Incoming event · 3
Uma mão, uma sessão, uma intensidade: uma impressão, não um resultado. O par mais próximo reportado foi completed contra mad, o próprio padrão de confirmação da documentação contra o exemplo dela para uma reunião a qual você não pôde participar: sucesso e falha, reportados como os dois mais próximos. O par de que o design depende foi então testado às cegas e separado 20/20. Os outros 101 pares seguem como parecem, sem nenhum teste.

A arquitetura tem três camadas, deliberadamente: nomes semânticos no código, o mapeamento nome→waveform na configuração (de modo que revisá-lo depois de um teste com usuários é uma edição, não um rebuild) e as quinze primitivas entregues por baixo. Uma ponte de design tokens, aplicada a um sentido que as ferramentas de design ainda não conseguem pré-visualizar. As duas camadas que entregamos são pequenas o bastante para serem lidas por inteiro:

Fig. 04.4 · não são cinco waveforms. São cinco significados. a camada que o SDK não entrega, declarada na configuração
events/DefaultEventSource.yaml · a camada semântica
events:
  - name: taskStarted
    # dispara uma vez, e estabelece do que trata cada pulso seguinte
  - name: taskTick
    # a batida, não uma mensagem — a ausência dela é que carrega sentido
  - name: taskNeedsInput
    # o único estado autorizado a interromper
  - name: taskCompleted
    # terminou, e deu certo
  - name: taskFailed
    # terminou, e não deu certo
events/extra/eventMapping.yaml · a camada de mapeamento
haptics:
  taskStarted:    { DEFAULT: sharp_state_change }
  taskTick:       { DEFAULT: subtle_collision }
  taskNeedsInput: { DEFAULT: knock }
  taskCompleted:  { DEFAULT: completed }
  taskFailed:     { DEFAULT: angry_alert }

  # completed e mad nunca são o par — eles foram
  # reportados como os dois padrões mais próximos no dispositivo,
  # e são os dois estados que jamais podem ser confundidos.
O mapeamento vive na configuração em vez de no código, e é por isso que uma revisão depois de um teste com usuários é uma edição e não um rebuild. É também uma demonstração viva do argumento: a arquitetura de tokens em três camadas do SDK é boa; ela simplesmente não tem uma camada semântica em cima.
// 05

Testar um sinal ambiente

Um sinal ambiente não pode testar a si mesmo: o autor já sabe qual estado escreveu no canal, e discriminação não é detecção. O aparato precisa remover esse saber.

O aparato cego escreve um estado terminal aleatório, completed ou failed, e só registra qual foi depois que o palpite é dito. Vinte tentativas em três rodadas, incluindo no ajuste de intensidade Subtle, aquele que um usuário em fluxo de fato manteria: 20 de 20. E uma vez, sem combinar, o vocabulário simplesmente foi usado por uma tarde de trabalho real, o que não é um resultado, mas é a razão pela qual o trabalho pareceu pronto.

Fig. 05 · o que foi testado, e o que foi apenas desenhado preenchido é feito · vazado é devido
completed vs failed, às cegas: 20/20 três rodadas · Subtle incluída · uma mão
previsões, datadas de antemão: 12 dois estudos desenhados · nenhum executado
P1–P6 para o protocolo de laboratório, F1–F6 para o modo de campo: cada uma escrita e datada antes de existir qualquer participante, de modo que uma execução futura é pontuada pelo registro, não pela memória.
// A metade honesta

Cada tentativa acima aconteceu em uma mão, e ela é a do designer. A aprendibilidade está em grande parte por testar: as tentativas medem se o design funciona, não se ele ensina. A retenção não foi testada. Os estudos que resolveriam as duas coisas estão desenhados, pré-registrados e à espera de mãos que não são as nossas, e o artefato diz isso nas próprias páginas em vez de deixar subentendido.

// 06

Publicar: os achados se voltam para nós mesmos

A publicação não foi um epílogo. Preparar o plugin para o Logi Marketplace virou as observações de campo contra o nosso próprio manifesto, e corrigiu ali o que o log havia reportado em todo o resto.

Fig. 06.1 · o laço que se fecha sobre nós mesmos fricção observada → decisão tomada, no nosso próprio plugin
o log 39 linhas até a submissão, observadas à vista de todos
o nosso manifesto decisões 37–42, corrigidas aqui primeiro
o Marketplace submetido: a rota de instalação que funciona, quando listar
outras mãos o estudo de campo, destravado quando listar
e o campo escreve as próximas linhas
Os cinco fechamentos, um a um
fricção 25 · o build só funcionava a partir de uma pasta decisão 37 · intermediários fixados: a armadilha do próprio scaffold, resolvida pela correção que o log propôs
fricção 27 · o scaffold declara dispositivos que excluem justamente o dispositivo de que o haptics precisa decisão 38 · o manifesto declara ActionsRing, corrigido no dia em que ganhou uma consequência visível
fricção 39 · o vocabulário de dispositivos aceito só existe dentro da DLL encontrado por reflection no preparo da submissão: um nome desconhecido é lido como None, em silêncio
a alegação de macOS · empacotada, nunca executada decisão 39 · a v1 declara apenas Windows: retirada sem teste em vez de submetida sem teste
fricção 41 · a referência do scaffold põe no pacote 12 arquivos do próprio Plugin Service, 9,5 MB de 9,5 MB; pack e verify deram OK; o QA da loja o devolveu decisão 41 · <Private>false</Private>, build numa pasta vazia, 33,6 KB; tínhamos lido a lista de arquivos na submissão sem compará-la com nada
O laço que mais importa: as fricções 33, 34 e 38 mostraram que a rota de instalação local reporta sucesso e não entrega nada; uma página no Marketplace é a rota de instalação que funciona, então publicar destrava o estudo de campo que esses defeitos bloqueavam.
Fig. 06.2 · a última mudança antes da submissão uma página de loja também é uma superfície
Variante de ícone V1: a barra transversal carrega o ritmo V1 · a barra é o ritmo cortada: agora uma linha do registro
Variante de ícone V2: um pulso atravessando a haste V2 · pulso atravessando a haste cortada: agora uma linha do registro
Variante de ícone V3: um T sobre três pontos, a escolhida V3 · a contagem sob a marca a escolhida: um T sobre três pontos
O ícone do Tacit no tamanho da loja, como submetido submetido a peça de quebra-cabeça do scaffold, aposentada · decisão 40
As três marcas foram desenhadas e julgadas em renders de 48 pixels, aproximadamente o tamanho que uma página de loja mostra. V1 e V2 não foram apagadas: elas vivem como linhas do registro ATÉ ONDE FOI do workspace, cada uma com o critério medido que a matou.
// 07

O que existe agora

Tudo abaixo é alcançável hoje. Escolha um estado e siga um evento real o caminho inteiro, pelas três camadas entregues, até a quarta: a que a mão lê.

Fig. 07 · de uma linha escrita ao polegar cada estação é um arquivo real · escolha um estado
  1. um escritor t7 startedt7 progresst7 needsInputt7 completedt7 failed uma linha acrescentada ao canal: sem SDK, sem C#, sem projeto de plugin
  2. camada semântica · events/DefaultEventSource.yaml - name: taskStarted- name: taskTick- name: taskNeedsInput- name: taskCompleted- name: taskFailed cinco nomes: nossos, não da plataforma
  3. camada de mapeamento · events/extra/eventMapping.yaml taskStarted: { DEFAULT: sharp_state_change }taskTick: { DEFAULT: subtle_collision }taskNeedsInput: { DEFAULT: knock }taskCompleted: { DEFAULT: completed }taskFailed: { DEFAULT: angry_alert } por dispositivo, com fallback DEFAULT: revisar é uma edição, não um rebuild
  4. primitiva · uma de quinze sharp_state_changesubtle_collisionknockcompletedangry_alert conjunto fixo: seleção, nunca síntese
  5. a quarta · não é camada no figure: one pulse, nothing to sequenceheartbeat: 15 s · TaskRunner.cs, not yamlinterval ×= 0.75 · floor 4 s · TaskRunner.csone pulse, then the channel closesSequence.Pulses(raise, "taskFailed", 3, 450) nada no sistema de tokens consegue expressar contagem ou intervalo
  6. o polegar uma marca seca, no zerouma batida discreta, a cada 15 suma batida na porta, fora do compassoum pulso cheiotrês, a 450 ms
cada estação acima executou · nenhuma inferida som ao toque · ritmo, nunca o atuador
Três camadas são entregues (um nome, um mapeamento, uma waveform), e cada uma é um arquivo que este repositório guarda hoje. A quarta camada é a que a mão de fato lê: a contagem, o intervalo e a contração vivem em código, porque nada no sistema de tokens consegue dizer três, nem 450 ms.
O plugin

Submetido ao Logi Marketplace em 3 de setembro de 2026; devolvido pelo QA da loja para um ajuste de empacotamento, reenviado em 26 de setembro e atualizado para 1.0.2 em 7 de outubro, em análise. Cinco estados semânticos, guiados por canal, sem aplicativo vinculado, e o par terminal separado 20/20 às cegas, no ajuste Subtle.

O registro

39 linhas de fricção · 40 decisões de design, cada uma com o que a forçou e o que ela preteriu · 12 previsões pré-registradas em dois desenhos de estudo · um workspace de evidências com 17 páginas, contados na submissão de 3 de setembro

A contribuição

Uma camada semântica para estado ao longo do tempo (cinco nomes, uma convenção compartilhada para um polegar, e uma camada de mapeamento que torna a revisão uma edição em vez de um rebuild), proposta apenas sobre evidência pública

// o entregável, hojeclonar · buildar · sentir · MIT · público

A listagem na loja está em análise; o código não está esperando. O código-fonte completo do plugin, sob MIT: o mesmo código a partir do qual o pacote da loja é gerado.

// 08

O produto é invisível. O julgamento não é.

O código aqui foi escrito por uma IA sob a direção de um designer, dito com todas as letras, porque o método faz parte do que está sendo mostrado. O que encolheu foi a distância entre uma decisão de design e um artefato testável: uma ideia sobre ritmo virou uma coisa que um polegar podia vetar no mesmo dia.

E um polegar é exatamente o limite: ele pode provar que um vocabulário funciona, nunca que ele ensina. A metade honesta (pré-registrada, datada, por fazer) não é uma ressalva. Numa superfície tão silenciosa quanto esta, ela é o design.