Documentação Abrangente pwE

pwE (Pray With Me) é um aplicativo revolucionário desenvolvido em Flutter, projetado especificamente para guiar o usuário na prática milenar da Lectio Divina (Leitura Orante). Em perfeita consonância com a fé católica e o Magistério da Igreja, o app une espiritualidade e alta tecnologia para criar um ambiente imersivo, focado e seguro de oração.

Princípios e Pilares do Projeto

Desenvolvido sob rígidas normas de arquitetura e design (conforme o tema oficial definido em app_theme.dart), o pwE é sustentado por três pilares fundamentais:

† Fidelidade Doutrinária

Todas as meditações, orações e reflexões propostas pela IA seguem rigorosamente o Magistério Católico. Nenhuma metáfora ou termo herético ou tecnológico é exposto ao usuário na experiência de meditação.

🔒 Privacidade Absoluta

Oração é intimidade. Com o motor Edge LLM, o processamento de IA pode ocorrer inteiramente de forma local no dispositivo do usuário, garantindo a soberania dos dados da alma.

📱 Design Imersivo

Utilização de mecânicas refinadas (como revelação gradual por scroll e gestos táteis de retenção) para eliminar distrações e guiar o usuário pelas etapas litúrgicas.

Clean Architecture & DDD

Para garantir que o negócio seja independente de tecnologias de banco de dados, frameworks ou APIs de terceiros, o pwE adota uma estrutura em camadas baseada em Clean Architecture e Domain-Driven Design (DDD).

Camadas Arquiteturais

A separação de responsabilidades assegura a facilidade de manutenção e alta cobertura de testes unitários:

  • Camada de Domínio (Domain): Contém as regras de negócio puras (Entities, UseCases e contratos definidos). Veja a entidade da Lectio Divina no arquivo lectio_session.dart:
    // lib/features/lectio_divina/domain/entities/lectio_session.dart
    class LectioSession {
      final String id;
      final DateTime date;
      final String passage;
      final String emotion;
      final String meditation;
      final String prayer;
      final String contemplationResolution;
      final int streakDays;
    
      const LectioSession({
        required this.id,
        required this.date,
        required this.passage,
        required this.emotion,
        required this.meditation,
        required this.prayer,
        required this.contemplationResolution,
        required this.streakDays,
      });
    }
  • Camada de Dados (Data): Contém a infraestrutura (Models, DataSources e as implementações concretas dos repositórios como em llm_remote_datasource.dart):
    // lib/features/lectio_divina/data/datasources/llm_remote_datasource.dart
    abstract class LlmRemoteDataSource {
      Future<MeditationModel> generateMeditation(MeditationRequestModel request);
    }
    
    class LlmRemoteDataSourceImpl implements LlmRemoteDataSource {
      final ApiClient apiClient;
    
      LlmRemoteDataSourceImpl({required this.apiClient});
    
      @override
      Future<MeditationModel> generateMeditation(MeditationRequestModel request) async {
        final response = await apiClient.post(
          '/v1/spiritual-advisor/meditate',
          data: request.toJson(),
        );
        return MeditationModel.fromJson(response.data);
      }
    }
  • Camada de Apresentação (Presentation): Contém a interface do usuário (Widgets e Pages) e a gestão de estado complexa baseada em **Bloc** (veja em lectio_bloc.dart):
    // lib/features/lectio_divina/presentation/bloc/lectio_bloc.dart
    class LectioBloc extends Bloc<LectioEvent, LectioState> {
      final GetGuidedMeditation getGuidedMeditation;
      final SaveSession saveSession;
    
      LectioBloc({
        required this.getGuidedMeditation,
        required this.saveSession,
      }) : super(LectioInitial()) {
        on<StartSessionEvent>(_onStartSession);
        on<AdvanceToStepEvent>(_onAdvanceToStep);
        on<CompleteSessionEvent>(_onCompleteSession);
      }
    
      void _onStartSession(StartSessionEvent event, Emitter<LectioState> emit) {
        emit(LectioInProgress(step: LectioStep.reading, passage: event.passage));
      }
    }
Injeção de Dependências

Todos os serviços, repositórios, use cases e blocs são registrados no container central de injeção localizado em injection_container.dart através do pacote **GetIt**:

// lib/core/di/injection_container.dart
final sl = GetIt.instance;

Future<void> init() async {
  // Features - Lectio Divina (BLoCs)
  sl.registerFactory(() => LectioBloc(getGuidedMeditation: sl(), saveSession: sl()));

  // Use cases
  sl.registerLazySingleton(() => GetGuidedMeditation(sl()));
  sl.registerLazySingleton(() => SaveSession(sl()));

  // Repositories
  sl.registerLazySingleton<IARepository>(() => IARepositoryImpl(remoteDataSource: sl()));

  // Data sources
  sl.registerLazySingleton<LlmRemoteDataSource>(() => LlmRemoteDataSourceImpl(apiClient: sl()));

  // External (Core)
  sl.registerLazySingleton(() => ApiClient());
}

Fluxo Imersivo da Lectio Divina

A Lectio Divina no pwE consolida as quatro etapas clássicas em uma visualização contínua usando a mecânica de CustomScrollView, eliminando a fadiga de telas:

Etapa 1

Lectio (Leitura)

O usuário lê com atenção o texto sagrado do dia (liturgia diária) ou digita/digitaliza uma passagem desejada.

Etapa 2

Meditatio (Meditação)

O app sugere pontos de meditação através da IA, conectando o texto sagrado a analogias e vivências de fé.

Etapa 3

Oratio (Oração)

O usuário responde à Palavra em forma de súplica, adoração ou agradecimento, com prompts baseados nos Salmos.

Etapa 4

Contemplatio (Ação)

O usuário traça uma resolução prática de vida e guarda a oração no silêncio do seu diário pessoal.

Componentes da UI Focada

Estes elementos de interface criam uma atmosfera de introspecção na página immersive_reading_page.dart:

  • ProgressiveTextReveal: Esconde trechos futuros por meio de desfoque (blur) até que o usuário faça scroll ou interaja com o texto, auxiliando a focar na leitura atual.
  • HoldToSaveButton: Exige pressionar o botão por 2 segundos contínuos com resposta tátil para assegurar a intencionalidade de avanço de etapa, bloqueando cliques indesejados no silêncio da oração.
  • ThinkingFeedback: Exibe mensagens motivacionais rotativas e espirituais enquanto a IA processa o prompt (Ex: "Unindo a sua oração aos coros celestes...").

Veja um exemplo prático de implementação do HoldToSaveButton, com lógica tátil e preenchimento gradual por tempo em Flutter:

// lib/features/lectio_divina/presentation/widgets/hold_to_save_button.dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class HoldToSaveButton extends StatefulWidget {
  final VoidCallback onTrigger;
  final String label;

  const HoldToSaveButton({Key? key, required this.onTrigger, required this.label}) : super(key: key);

  @override
  _HoldToSaveButtonState createState() => _HoldToSaveButtonState();
}

class _HoldToSaveButtonState extends State<HoldToSaveButton> with SingleTickerProviderStateMixin {
  late AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      duration: const Duration(seconds: 2),
    );
    _controller.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        HapticFeedback.heavyImpact();
        widget.onTrigger();
        _controller.reset();
      }
    });
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onLongPressStart: (_) {
        HapticFeedback.mediumImpact();
        _controller.forward();
      },
      onLongPressEnd: (_) {
        if (_controller.status != AnimationStatus.completed) {
          _controller.reverse();
        }
      },
      child: AnimatedBuilder(
        animation: _controller,
        builder: (context, child) {
          return Container(
            height: 56,
            width: double.infinity,
            decoration: BoxDecoration(
              borderRadius: BorderRadius.circular(12),
              border: Border.all(color: Colors.amber.shade700, width: 1.5),
              gradient: LinearGradient(
                colors: [Colors.amber.shade800, Colors.amber.shade900],
                stops: [0.0, _controller.value],
              ),
            ),
            child: Center(
              child: Text(
                _controller.value > 0 ? 'Mantenha pressionado... ${(_controller.value * 100).toInt()}%' : widget.label,
                style: const TextStyle(fontWeight: FontWeight.bold, color: Colors.white),
              ),
            ),
          );
        },
      ),
    );
  }
}

Sistema de Maturidade (Fading Scaffolding)

Para não sobrecarregar o usuário e encorajá-lo a rezar de forma autônoma, o pwE implementa a estratégia inovadora de Fading Scaffolding. A IA adapta o nível de verbosidade e a profundidade de seus prompts conforme o progresso espiritual do fiel.

Os Três Estágios da Maturidade

0 a 7 dias

Beginner (Iniciante)

Orientação didática detalhada. A IA ensina o significado de cada etapa e apresenta analogias ilustrativas detalhadas para iniciantes na prática.

8 a 21 dias

Practitioner (Praticante)

Foco em provocações de fé. Elimina as introduções acadêmicas e propõe pontos diretos de questionamento pessoal e aplicação na caridade diária.

Constância 21+ dias

Contemplative (Contemplativo)

Minimalismo e Silêncio. A IA fornece no máximo duas linhas inspiradoras baseadas na tradição dos Padres da Igreja, estimulando o silêncio interior.

Maturidade Manual

A evolução ocorre naturalmente com a contagem de constância (streak). Contudo, o usuário pode definir manualmente seu nível de maturidade nas configurações caso deseje um apoio mais contundente ou prefira o silêncio contemplativo imediatamente.

Invasão Zero & Privacidade: Edge LLM

Um dos maiores diferenciais do pwE é a sua capacidade de operar de maneira 100% autônoma e privada. Graças ao motor Edge LLM, as orações escritas na intimidade do quarto nunca precisam sair do smartphone.

Especificações Técnicas de Inferência Local

  • Modelo Utilizado: Gemma 4 (classe 2B) otimizado e quantizado para dispositivos móveis.
  • Engine: Baseado na biblioteca **LiteRT-LM** rodando em canais de métodos nativos (`MethodChannel`) rápidos e eficientes. Gerenciado por llm_local_datasource.dart.
  • Hardware Mínimo: Smartphones com 4GB de RAM livre, suporte a aceleração gráfica por GPU/NPU e ~1.3GB de espaço livre para o modelo quantizado.
  • Indicador Visual de Privacidade: Sempre que a IA é processada localmente, o app apresenta um Escudo Dourado, atestando o isolamento do processamento.

Abaixo está a implementação prática de LlmLocalDataSourceImpl, demonstrando a invocação nativa e o fallback automático para a nuvem em caso de erro:

// lib/features/lectio_divina/data/datasources/llm_local_datasource.dart
import 'package:flutter/services.dart';
import 'package:pwe/features/lectio_divina/data/models/meditation_model.dart';
import 'package:pwe/features/lectio_divina/data/models/meditation_request_model.dart';
import 'package:pwe/features/lectio_divina/data/datasources/llm_remote_datasource.dart';

abstract class LlmLocalDataSource {
  Future<MeditationModel> generateMeditation(MeditationRequestModel request);
}

class LlmLocalDataSourceImpl implements LlmLocalDataSource {
  static const MethodChannel _channel = MethodChannel('pwe.app/gemma-inference');
  final LlmRemoteDataSource remoteFallback;
  final bool isGemmaEnabled;

  LlmLocalDataSourceImpl({
    required this.remoteFallback,
    required this.isGemmaEnabled,
  });

  @override
  Future<MeditationModel> generateMeditation(MeditationRequestModel request) async {
    if (!isGemmaEnabled) {
      return remoteFallback.generateMeditation(request);
    }

    try {
      final String? response = await _channel.invokeMethod<String>(
        'executeGemmaInference',
        {
          'prompt': request.toPrompt(),
          'max_tokens': 200,
          'temperature': 0.3,
        },
      );

      if (response == null || response.isEmpty) {
        throw PlatformException(code: 'EMPTY_RESPONSE', message: 'Gemma local retornou vazio.');
      }

      return MeditationModel.fromLocalText(response, request.passage);
    } catch (e) {
      // Fallback transparente em caso de indisponibilidade ou falha de inferência local
      return remoteFallback.generateMeditation(request);
    }
  }
}
Estratégia Edge-First com Fallback Automático

Se o modelo local não for encontrado ou se o dispositivo não comportar a inferência, o aplicativo faz uma transição transparente e segura para a API na nuvem (Gemini), assegurando a continuidade da Lectio Divina sem interrupções.

Captura de Passagem Bíblica por Foto (OCR)

Além da digitação de passagens e da liturgia do dia coletada dinamicamente, o pwE permite a captura instantânea de textos bíblicos impressos usando inteligência artificial visual local.

Funcionamento e Arquitetura do OCR

Integrando o Google ML Kit Vision, o processo é inteiramente offline e ágil:

1. Captura & Edição

O usuário fotografa o trecho da Bíblia física e edita a imagem (corta e rotaciona) na tela de pré-processamento para focar exclusivamente no texto litúrgico.

2. Extração de Texto

O processamento OCR extrai as sentenças com precisão, aplicando um refinamento automático de ortografia e pontuação para a leitura fluida.

3. Identificação Remota

O trecho reconhecido é enviado para o analisador de contexto bíblico, que detecta automaticamente o livro, capítulo e versículo correspondente.

Abaixo está o exemplo prático de integração com o ML Kit OCR para extração offline de texto bíblico nas páginas físicas da Bíblia:

// lib/features/lectio_divina/data/datasources/ocr_local_datasource.dart
import 'dart:io';
import 'package:google_ml_kit/google_ml_kit.dart';

abstract class OcrLocalDataSource {
  Future<String> extractTextFromImage(File imageFile);
}

class OcrLocalDataSourceImpl implements OcrLocalDataSource {
  final TextRecognizer _textRecognizer = GoogleMlKit.vision.textRecognizer();

  @override
  Future<String> extractTextFromImage(File imageFile) async {
    final inputImage = InputImage.fromFile(imageFile);
    try {
      final RecognizedText recognizedText = await _textRecognizer.processImage(inputImage);
      
      // Filtra e limpa o texto para remover ruídos e marcas indesejadas do reconhecimento
      String rawText = recognizedText.text;
      return _refineBiblicalText(rawText);
    } catch (e) {
      throw Exception('Erro ao processar imagem via ML Kit OCR: ${e.toString()}');
    }
  }

  String _refineBiblicalText(String input) {
    return input
        .replaceAll('\n', ' ')
        .replaceAll(RegExp(r'\s+'), ' ')
        .trim();
  }

  void dispose() {
    _textRecognizer.close();
  }
}

Contratos de Integração & API (Swagger)

O pwE segue os mais altos padrões de arquitetura de software de mercado para as suas conexões locais e na nuvem. A seguir, explore a documentação interativa e orientada a código para os principais endpoints e canais de plataforma do ecossistema.

Envia o texto da Escritura Sagrada (seja via digitação direta, liturgia diária ou capturado localmente por OCR) e o estado afetivo-espiritual do fiel para o orientador espiritual de IA na nuvem. A resposta é formulada estritamente sob as diretrizes do Magistério e do Fading Scaffolding de maturidade.

Parâmetros do Body (JSON)

Parâmetro Tipo Descrição
passage* obrigatório string O texto bíblico completo do versículo ou capítulo selecionado para a meditação.
emotion string Disposição emocional do fiel (ex: "ansioso", "grato", "tribulado") para contextualizar a resposta pastoral.
maturity_level* obrigatório string Nível de progresso no app que define a verbosidade e profundidade (beginner, practitioner, contemplative).
import 'package:pwe/core/network/api_client.dart';
import 'package:pwe/features/lectio_divina/data/models/meditation_request.dart';
import 'package:pwe/features/lectio_divina/data/models/meditation_response.dart';

final apiClient = GetIt.I<ApiClient>();

final request = MeditationRequest(
  passage: "O Senhor é o meu pastor, nada me faltará. Em verdes pastagens me faz repousar. (Salmo 22, 1-2)",
  emotion: "ansioso",
  maturityLevel: MaturityLevel.beginner,
);

final Either<Failure, MeditationResponse> result = 
    await apiClient.post('/v1/spiritual-advisor/meditate', data: request.toJson());

result.fold(
  (failure) => print('Erro espiritual/rede: ${failure.message}'),
  (meditation) => print('Meditação gerada: ${meditation.guidance}'),
);
curl -X POST "https://api.pwe.app/v1/spiritual-advisor/meditate" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer SEU_TOKEN_AQUI" \
  -d '{
    "passage": "O Senhor é o meu pastor, nada me faltará. (Salmo 22, 1)",
    "emotion": "ansioso",
    "maturity_level": "beginner"
  }'
{
  "passage": "O Senhor é o meu pastor, nada me faltará. Em verdes pastagens me faz repousar. Conduz-me junto às águas refrescantes, restaura as forças de minha alma. (Salmo 22, 1-3)",
  "emotion": "ansioso",
  "maturity_level": "beginner"
}
{
  "status": "success",
  "data": {
    "passage_reference": "Salmo 22, 1-3",
    "maturity_applied": "beginner",
    "spiritual_guidance": "Meu filho em Cristo, a ansiedade tenta turvar as águas da tua alma, mas o Bom Pastor te convida ao repouso no Seu amor. Diante da agitação do mundo, lembra-te de que Ele tem o controle absoluto de todas as coisas. Entrega-Lhe as tuas preocupações no silêncio da oração. Nas palavras de São Padre Pio: 'Reze, espere e não se preocupe. A preocupação é inútil. Deus é misericordioso e ouvirá sua oração.' Que o teu coração descanse hoje na certeza da Providência Divina.",
    "reflection_points": [
      "Como você tem buscado as 'verdes pastagens' da oração no meio das tribulações cotidianas?",
      "De que maneira a entrega confiante ao Bom Pastor pode acalmar a tempestade da sua ansiedade hoje?",
      "Faça um ato de fé simples: 'Jesus, eu confio em Vós, mesmo quando não vejo o caminho.'"
    ]
  }
}

Consome a API oficial para carregar os textos litúrgicos da Igreja Católica Apostólica Romana válidos para a data corrente ou uma data opcional. Retorna de forma estruturada a Primeira Leitura, Salmo Responsorial, Segunda Leitura (se houver) e o Evangelho para apoiar a Lectio Divina.

Parâmetros da Query (Request URL)

Parâmetro Tipo Descrição
date string Data alvo no formato ISO-8601 YYYY-MM-DD. O padrão é a data atual do servidor.
translation string Tradução e versão bíblica desejada (ex: cnbb, ave_maria, jerusalem). Padrão: cnbb.
import 'package:pwe/features/lectio_divina/domain/repositories/liturgy_repository.dart';
import 'package:pwe/features/lectio_divina/domain/entities/liturgical_day.dart';

final liturgyRepo = GetIt.I<LiturgyRepository>();

final Either<Failure, LiturgicalDay> result = 
    await liturgyRepo.getTodayLiturgy(translation: 'cnbb');

result.fold(
  (failure) => print('Erro ao coletar liturgia: ${failure.message}'),
  (liturgy) => print('Evangelho do dia (${liturgy.reference}): ${liturgy.content}'),
);
curl -X GET "https://api.pwe.app/v1/liturgy/today?translation=cnbb" \
  -H "Accept: application/json"
{
  "date": "2026-05-21",
  "liturgical_season": "Tempo Pascal",
  "liturgical_color": "Branco",
  "celebration": "Quinta-feira da 7ª Semana da Páscoa",
  "readings": {
    "first_reading": {
      "reference": "Atos 22, 30; 23, 6-11",
      "title": "Paulo testemunha em Jerusalém e é enviado a Roma",
      "content": "Naqueles dias, querendo saber com certeza de que os judeus o acusavam, o comandante soltou Paulo e mandou reunir os sumos sacerdotes e todo o conselho dos anciãos..."
    },
    "psalm": {
      "reference": "Salmo 15 (16)",
      "response": "Guardai-me, ó Deus, porque em vós me refugio!",
      "verses": [
        "Preservai-me, ó Deus, porque em vós me refugio! Eu digo ao Senhor: 'Vós sois o meu Deus'.",
        "Ó Senhor, vós sois a minha herança e o meu cálice; nas vossas mãos está o meu destino."
      ]
    },
    "evangelho": {
      "reference": "João 17, 20-26",
      "title": "Que eles sejam um em nós, para que o mundo creia",
      "content": "Naquele tempo, Jesus levantou os olhos ao céu e rezou, dizendo: 'Não rogo somente por eles, mas também por aqueles que vão creer em mim pela sua palavra...'"
    }
  }
}

Canal nativo de comunicação de plataforma (MethodChannel) para delegar de forma assíncrona o processamento de inferência do Large Language Model (Gemma 2B quantizado via LiteRT-LM) para execução 100% isolada e offline no smartphone do usuário. Garante soberania e privacidade inviolável.

Argumentos de Entrada (Method Call)

Argumento Tipo Descrição
prompt* obrigatório string Prompt contendo a passagem e o contexto católico estrito a ser alimentado no Gemma local.
max_tokens int Limite de tokens a serem gerados pelo modelo. Padrão: 150.
temperature double Fator de aleatoriedade térmica do decodificador. Padrão: 0.3.
import 'package:flutter/services.dart';

const MethodChannel _channel = MethodChannel('pwe.app/gemma-inference');

Future<String?> generateLocalGuidance({
  required String prompt,
  int maxTokens = 150,
  double temperature = 0.3,
}) async {
  try {
    final String? result = await _channel.invokeMethod<String>(
      'executeGemmaInference',
      {
        'prompt': prompt,
        'max_tokens': maxTokens,
        'temperature': temperature,
      },
    );
    return result;
  } on PlatformException catch (e) {
    print("Erro na inferência local Gemma: ${e.message}");
    return null;
  }
}
package com.pwe.app.inference

import android.content.Context
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import com.google.mediapipe.tasks.genai.llminference.LlmInference

class GemmaInferencePlugin : FlutterPlugin, MethodChannel.MethodCallHandler {
    private lateinit var channel: MethodChannel
    private var llmInference: LlmInference? = null

    override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
        channel = MethodChannel(binding.binaryMessenger, "pwe.app/gemma-inference")
        channel.setMethodCallHandler(this)
    }

    override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
        if (call.method == "executeGemmaInference") {
            val prompt = call.argument<String>("prompt") ?: ""
            val maxTokens = call.argument<Int>("max_tokens") ?: 150
            val temp = call.argument<Double>("temperature") ?: 0.3

            try {
                // Inferência executada localmente de forma isolada
                val response = llmInference?.generateResponse(prompt)
                result.success(response)
            } catch (e: Exception) {
                result.error("GENAI_ERROR", e.localizedMessage, null)
            }
        } else {
            result.notImplemented()
        }
    }
}
{
  "prompt": "Contexto: Atue como um orientador espiritual católico fiel ao Magistério da Igreja. Passagem: João 17, 20-26. Gere 2 pontos curtos de meditação.",
  "max_tokens": 150,
  "temperature": 0.3
}
{
  "status": "success",
  "engine": "LiteRT-LM (Gemma 2B)",
  "inference_time_ms": 342,
  "result": "1. Unidade na Caridade: Jesus roga ao Pai por nossa unidade como sinal visível da Sua missão divina. Rezemos pela coesão da Igreja hoje.\n2. O Amor Divino em Nós: A oração de Cristo pede que o amor com que o Pai O amou habite em nós. Deixemo-nos possuir por esse amor no silêncio da Contemplatio."
}

Decisões de Arquitetura (ADRs)

O pwE mantém registros históricos das principais decisões de arquitetura de software (localizados na pasta docs/adr/), garantindo rastreabilidade técnica.

Status: Aceito | Objetivo: Separar a lógica do negócio (pureza do domínio) de agentes de infraestrutura.

Decidiu-se que a camada de Domain não possui qualquer importação do framework Flutter ou Firebase. Qualquer comunicação externa é definida via contratos de interface e resolvida dinamicamente no container do GetIt.

A separação de responsabilidades permite que toda a inteligência do aplicativo seja exaustivamente testada em ambiente de console simples, reduzindo o tempo de build em CI/CD.

Status: Aceito | Objetivo: Garantir estabilidade e velocidade na execução local do Gemma 4.

Foram avaliados pacotes prontos como `flutter_gemma` e `mediapipe_genai`. Contudo, devido a relatos de OOM (Out of Memory) e falta de suporte para threads em segundo plano no iOS/Android, optamos por criar canais nativos de plataforma (**MethodChannel** no Android com Kotlin utilizando a API LiteRT-LM nativa).

Isso permite usar aceleração física por GPU/NPU real dos chips modernos com total estabilidade.

Status: Proposto | Objetivo: indexação 100% offline e livre de vazamento de dados de orações.

O RAG precisa vetorializar as anotações do diário pessoal do usuário para busca semântica de longo prazo. Fazer isso na nuvem geraria vazamento de intimidade de fé.

A decisão foi utilizar o **MobileBERT** quantizado no dispositivo via LiteRT, permitindo gerar embeddings de 384 dimensões em milissegundos e sem conexão de rede.

Status: Aceito | Objetivo: Eliminar acoplamento excessivo e concorrência de UI no BLoC central.

O LectioBloc monolítico foi decomposto em 6 sub-BLoCs com responsabilidades atômicas e imutáveis: LectioSessionBloc, DailyLiturgyBloc, ExegesisBloc, PrayerJournalBloc, AiOratioBloc e SubscriptionBloc. Essa segregação elevou a testabilidade e viabilizou mais de 930 testes automatizados.

Status: Aceito | Objetivo: Garantir soberania espiritual e conformidade com privacidade por design.

Qualquer chamada a modelos LLM remotos exige consentimento prévio e transparente do usuário via diálogo dedicado. O app oferece alternativa clara para a prática da Lectio Divina puramente contemplativa sem qualquer IA ou uso de IA 100% on-device (Escudo Dourado).

Status: Aceito | Objetivo: Blindagem total das orações íntimas e reflexões do Caderno Espiritual.

Todas as caixas de persistência Hive contendo anotações da Lectio Divina são criptografadas em repouso com algoritmo AES-256-GCM, vetor de inicialização (IV) aleatório e chave derivada por PBKDF2 ancorada no Android Keystore e Apple Keychain.

Status: Aceito | Objetivo: Higiene arquitetural e bloqueio de use cases de simulação em produção.

A feature de cofre de segurança teve seu contêiner de injeção de dependência purgado do core e isolado em security_vault_injection_container.dart. Use cases de teste e bypass foram blindados sob if (!kDebugMode) para evitar brechas em builds de release.

Status: Aceito | Objetivo: Respeitar a pedagogia da Lectio Divina e adequar telas ao Design System Sacro.

A seleção de motor de IA foi desacoplada do cold start e reposicionada de forma orgânica ao término do onboarding (FTUE), adotando o Design System Sacro (Pergaminho Nobre / Shalom), tipografia Cinzel/Lora e divisores com cruz.

Status: Aceito | Objetivo: Estruturar governança técnica autônoma e concorrente para 16 especialistas.

Formalização da arquitetura multiagente com subagentes assíncronos declarados em .agents/agents/, com esquemas YAML padronizados, isolamento de workspaces e ferramentas especializadas, operando nas frentes de Engenharia, Teologia, FinOps, Design/A11y, QA e DevOps.

Histórico de Refatorações (Logs)

Acompanhe os logs técnicos de grandes evoluções e melhorias de engenharia aplicadas ao projeto (encontrados em docs/refactor/).

O download de um modelo de 1.3GB é crítico em redes móveis de internet. A refatoração implementou:

  • Download Particionado: Uso de buffers estáveis para evitar OOM durante a escrita local.
  • Validação de Integridade: Cálculo de hash SHA256 pós-download antes do descompactamento.
  • GlobalDownloadTracker: Um widget que flutua acima de todo o aplicativo mostrando o percentual do progresso, permitindo que o usuário navegue ou reze enquanto o download é concluído em background.

O processador cru do ML Kit capturava ruídos gráficos das bordas e sombras das páginas físicas da Bíblia. A refatoração introduziu:

  • Binarização por Limiar Adaptativo: Conversão rápida da foto capturada para preto e branco de alto contraste via Canvas nativo antes de submetê-la ao OCR.
  • Algoritmo de Correção Lexical: Um micro-parser local que cruza o texto capturado com expressões litúrgicas comuns para corrigir falhas sutis de reconhecimento de caracteres.

Resolução de Problemas (Troubleshooting)

Seção interativa destinada a engenheiros e testadores para resolver rapidamente problemas comuns de ambiente e build no projeto pwE.

Ocorrem erros quando o Gradle Wrapper ou o Gradle Plugin do Android não coincidem com o nível exigido de SDK. Para atualizar para uma versão estável e recomendada:

# Navegue até o diretório android do projeto
cd android
# Atualize o Gradle Wrapper para a versão estável compatível (ex: 8.13)
./gradlew wrapper --gradle-version 8.13

Certifique-se também de que o arquivo gradle-wrapper.properties possua o link de distribuição devidamente configurado para a versão estável em HTTPS.

Para garantir que o arquivo binário do modelo `.litertlm` (com ~1.3GB) não foi corrompido durante o download opt-in do usuário, o app valida o hash SHA256.

Caso o carregamento falhe continuamente, confirme a assinatura configurada no seu painel do Firebase Remote Config:

# Variáveis esperadas no Remote Config
enable_gemma_local: true
gemma_model_sha256: "COLOCAR_HASH_SHA256_REAL_DO_MODELO_AQUI"
gemma_model_download_url: "https://seu-cdn-seguro.com/models/gemma-2b-it.litertlm"

Se o hash diferir, o `GemmaLocalDataSource` rejeitará a inicialização local e acionará imediatamente o fallback para o Gemini na nuvem por segurança.

Ao rodar testes unitários e de integração de estado no BLoC (ex: `lectio_bloc_test.dart`), certifique-se de injetar os usecases mockados via **Mockito** ou **Mocktail** e registrar no GetIt de testes para evitar a chamada a APIs reais de rede.

# Execute todos os testes unitários do projeto de forma limpa
flutter test --coverage

Diagrama de Contexto e Container (C4 Model)

Este diagrama do modelo C4 detalha as integrações de alto nível do aplicativo pwE com serviços locais e externos da nuvem.

graph TB
    User((Usuário Católico))
    pwE[App pwE - Mobile]
    
    subgraph External_Systems [Sistemas Externos]
        Firebase[Firebase: Auth, Firestore, Crashlytics]
        LLM[IA: OpenAI / Gemini / Gemma]
        BibleAPI[API de Bíblia / Liturgia Diária]
        MLKit[Google ML Kit - OCR Local]
    end

    User -->|Pratica Lectio Divina| pwE
    pwE -->|Autentica e Sincroniza| Firebase
    pwE -->|Solicita Meditações/Orações| LLM
    pwE -->|Busca Passagens e Liturgia| BibleAPI
    pwE -->|Processa Imagens Offline| MLKit
        

Diagrama de Sequência da Sessão

Visualize o ciclo de vida completo de uma sessão de oração, acompanhando o tráfego de dados e comandos pelas camadas do software.

sequenceDiagram
    autonumber
    participant U as Usuário
    participant UI as Presentation (Bloc/UI)
    participant D as Domain (UseCases)
    participant R as Data (Repositories/DS)
    participant IA as IA (Gemini/Gemma)
    participant B as DB (Firestore/Hive)

    U->>UI: Inicia Sessão (Informa Emoção)
    UI->>D: SuggestPassageUseCase(emotion)
    D->>R: IARepository.getSuggestion()
    R->>IA: Prompt (Spiritual Advisor Context)
    IA-->>R: Sugestão de Passagem + Motivo
    R-->>D: DailyPassage Suggestion
    D-->>UI: LectioInProgress(suggestedPassage)
    
    U->>UI: Lê e Inicia momento Lectio
    UI->>U: Exibe Texto da Escritura
    
    U->>UI: Avança para Meditatio
    UI->>D: GetGuidedMeditationUseCase(passage)
    D->>R: IARepository.getMeditationPoints()
    R->>IA: Prompt (Meditatio alinhada ao Magistério)
    IA-->>R: Pontos de Meditação Concreta
    R-->>D: MeditationPoints
    D-->>UI: LectioInProgress(guidingPrompt)
    
    U->>UI: Avança para Oratio & Escreve preces
    UI->>D: GetPrayerPromptUseCase()
    D->>R: IARepository.getPrayerInspiration()
    R->>IA: Prompt baseado na Tradição Católica
    IA-->>R: Inspiração de Oração
    R-->>D: PrayerPrompt
    D-->>UI: LectioInProgress(prayerPrompt)

    U->>UI: Escreve resolução (Contemplatio)
    U->>UI: Conclui Sessão (HoldToSaveButton)
    UI->>D: SaveSessionUseCase(session)
    D->>R: SessionRepository.save()
    R->>B: Persiste localmente e na nuvem
    B-->>R: Sucesso
    R-->>D: Sucesso
    D-->>UI: LectioCompleted
    UI->>U: Exibe Ecrã de Celebração de Streak
        

Máquina de Estados: LectioBloc

Gerenciamento rigoroso de estados e fluxos de tela assíncronos no pwE utilizando a tríade Evento, Estado e BLoC.

stateDiagram-v2
    [*] --> LectioInitial: Inicialização da Feature

    LectioInitial --> LectioInProgress_Lectio: StartSessionEvent

    state LectioInProgress {
        [*] --> LectioInProgress_Lectio
        
        LectioInProgress_Lectio --> LectioInProgress_Meditatio: AdvanceToStep(1)
        LectioInProgress_Meditatio --> LectioInProgress_Oratio: AdvanceToStep(2)
        LectioInProgress_Oratio --> LectioInProgress_Contemplatio: AdvanceToStep(3)
        
        LectioInProgress_Contemplatio --> [*]: CompleteSessionEvent
    }

    LectioInProgress --> LectioCompleted: Session Saved Successfully
    LectioInProgress --> LectioError: Failure (API/Network/DB)
    
    LectioError --> LectioInitial: Reset / Tentar Novamente
    LectioCompleted --> [*]
        

Estrutura de Pastas e Mapeamento Clean Architecture

A árvore de diretórios do repositório pwE reflete a intenção das camadas e o isolamento de domínios.

Árvore de Pastas

lib/
├── core/                        # Núcleo compartilhado
│   ├── di/                      # Container GetIt (injection_container.dart)
│   ├── error/                   # Failures e Exceptions (Either)
│   ├── network/                 # REST Clients e configurações de rede
│   ├── routes/                  # Configurações de rotas com GoRouter
│   └── theme/                   # Estilos visuais e paletas do aplicativo
├── features/                    # Módulos de domínio isolados
│   ├── lectio_divina/           # Domínio principal de Leitura Orante
│   │   ├── data/                # DTOs, Repositórios e fontes de dados
│   │   ├── domain/              # Entidades, regras de negócios e UseCases
│   │   └── presentation/        # Controladores BLoC, widgets e páginas
│   ├── auth/                    # Gerenciamento de login e segurança
│   └── profile/                 # Controle de maturidade e preferências
└── main.dart                    # Configurações e bootstrap do app