// 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ápticoExperiência do DesenvolvedorPesquisa de CampoPlugin 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.
// O vocabulário rodando: batidas comprimidas de 15 s; o intervalo de 450 ms do failed é real
_VISÃO GERAL
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.
_A PERGUNTA// 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.
_PREMISSA// 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
WindowsTerminalclaudeFigmachromeexplorer
● 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 externoo ator, não a plateia
Marketplacenunca percorrido
nada construído aqui estava publicado na época da sonda
registra uma fonte de eventos, HasHapticMapping declarado
Logi Plugin Servicecarrega aquilo para onde é apontado
um arquivo em que qualquer coisa pode escrever
TacitDefaultEventSource.yaml · eventMapping.yaml
Três escritoresuma linha de texto cada
MX Master 4um atuador
o polegar
a mesma pessoa, nas duas faixas ao mesmo tempoum arquivo .link, escrito num diretório que ainda não existia · fricção 09um nome semântico resolve para uma das quinze waveforms
A entrega não tem escopoo pulso chega qualquer que seja o dono da tela, e um perfil de terceiro ativo não o suprime14 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.
_NOTAS DE CAMPO// 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 blueprinto que chega ao usuário fica uma linha acima do que a plataforma carrega
InstalarConfigurarIniciarEnquanto rodaPrecisa de vocêTerminaEvidênciao que de fato chega ao usuárioum card que não nomeia nada sobre o que reportaum único liga/desliga, dentro da preferência de intensidadeum pulsosharp_state_changeuma batida, a cada 15 ssubtle_collisionuma batida na porta, fora do compassoknockum pulso, ou três a 450 mscompleted / angry_alertUsuárioo que a pessoa fazinstala o pluginprocura as configurações deleinicia uma tarefa longatroca de aplicativo, e trabalhavolta para responderlê o desfecho no polegarLinha de interaçãoPalcoo que o usuário poderia ver ou sentir acontecendogerenciador de plugins do Options+o Tacit aparece, e não diz nadadiálogo Plugin Settings: metadados do manifesto, e o idioma em que o diálogo está escritoo atuador disparao atuador dispara na batidao atuador dispara fora da batidao atuador disparaLinha de visibilidadeBastidoreso que roda onde o usuário não vêo Logi Plugin Service carrega o plugin a partir de um arquivo .linkarmazenamento de configurações, criptografado e com chave, sem superfícieum 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 internaSuportea plataforma por baixo, e o que ela carrega ou nãoLogiPluginTool · PluginApi.dllMarketplace, nunca percorridoAPI de configurações do plugin: ActionEditor, alcançável apenas a partir de uma ação vinculadaDefaultEventSource.yamlcinco nomes semânticos: nossos, não da plataformaHasHapticMappingdeclarado no LoupedeckPackage.yamleventMapping.yamlum nome semântico para uma primitivaquinze 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 tentado3A ferramenta reporta um estado que não é verdadeirodecide se o desenvolvedor confia no que construiu4O primeiro build do primeiro projeto não compiladecide se existe um plugin, para começo de conversa5Quinze 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 achadosapenas acréscimo; a curadoria veio depois e em separado
39 linhas até a submissão, cruas33 lacuna de DX5 lacuna nossa1 indefinida
→
5achados de documentaçãolegíveis em páginas públicas em cerca de um minuto cada, e os cinco produzem um único reparo
5achados de superfície do produtoalcançá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.
_VOCABULÁRIO// 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 eixoposição = tempo · altura = intensidade
starteduma marca seca, no zero
progressuma batida discreta, a cada 15 s
needsInputuma batida na porta, fora do compasso
completedum pulso cheio
failedtrês, a 450 ms
som ao toque · ritmo, nunca o atuadorvibraçã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áticacompleted · 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 realo 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 confundidosquinze primitivas, ouvidas em sequência · 4 dos 105 pares soaram próximos
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._EVIDÊNCIA// 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 desenhadopreenchido é feito · vazado é devido
completed vs failed, às cegas: 20/20três rodadas · Subtle incluída · uma mão
previsões, datadas de antemão: 12dois 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.
_PUBLICAÇÃO// 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 mesmosfricção observada → decisão tomada, no nosso próprio plugin
o log39 linhas até a submissão, observadas à vista de todos
→
o nosso manifestodecisões 37–42, corrigidas aqui primeiro
→
o Marketplacesubmetido: a rota de instalação que funciona, quando listar
→
outras mãoso 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ãouma página de loja também é uma superfície
V1 · a barra é o ritmocortada: agora uma linha do registro
V2 · pulso atravessando a hastecortada: agora uma linha do registro
V3 · a contagem sob a marcaa escolhida: um T sobre três pontos
submetidoa 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._RESULTADOS// 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 polegarcada estação é um arquivo real · escolha um estado
um escritort7 startedt7 progresst7 needsInputt7 completedt7 faileduma linha acrescentada ao canal: sem SDK, sem C#, sem projeto de plugin
camada semântica · events/DefaultEventSource.yaml- name: taskStarted- name: taskTick- name: taskNeedsInput- name: taskCompleted- name: taskFailedcinco nomes: nossos, não da plataforma
camada de mapeamento · events/extra/eventMapping.yamltaskStarted: { 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
primitiva · uma de quinzesharp_state_changesubtle_collisionknockcompletedangry_alertconjunto fixo: seleção, nunca síntese
a quarta · não é camadano 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
o polegaruma 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 inferidasom 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.
_REFLEXÃO
// 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.