@polymorphism-tech/morph-spec 7.4.0 → 7.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +2 -2
  2. package/bin/morph-spec.js +18 -0
  3. package/claude-plugin.json +1 -1
  4. package/docs/CHEATSHEET.md +1 -1
  5. package/docs/QUICKSTART.md +1 -1
  6. package/framework/CLAUDE.md +9 -4
  7. package/framework/MORPH.md +4 -4
  8. package/framework/agents.json +1 -1
  9. package/framework/commands/create-umbraco-lp.md +35 -0
  10. package/framework/commands/migrate-lp-to-umbraco.md +33 -0
  11. package/framework/commands/morph-archive.md +17 -27
  12. package/framework/commands/morph-preflight.md +10 -5
  13. package/framework/hooks/claude-code/core/enforce-phase-writes.js +115 -90
  14. package/framework/hooks/claude-code/core/protect-spec-files.js +32 -30
  15. package/framework/hooks/claude-code/core/state-sync.js +16 -48
  16. package/framework/hooks/shared/state-reader.js +74 -8
  17. package/framework/hooks/shared/state-writer.js +124 -0
  18. package/framework/rules/infrastructure-standards.md +4 -4
  19. package/framework/rules/morph-task-tracking.md +69 -49
  20. package/framework/rules/morph-workflow.md +192 -191
  21. package/framework/skills/core/create-umbraco-lp/SKILL.md +272 -0
  22. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +531 -0
  23. package/framework/skills/core/morph-apply/SKILL.md +15 -15
  24. package/framework/skills/core/morph-brainstorming/SKILL.md +5 -5
  25. package/framework/skills/core/morph-eval/SKILL.md +1 -1
  26. package/framework/skills/core/morph-implement/SKILL.md +5 -4
  27. package/framework/skills/core/morph-plan/SKILL.md +6 -6
  28. package/framework/skills/core/morph-proposal/SKILL.md +8 -7
  29. package/framework/skills/core/morph-review/SKILL.md +3 -3
  30. package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
  31. package/framework/skills/core/morph-standards/SKILL.md +1 -1
  32. package/framework/standards/STANDARDS.json +80 -8
  33. package/framework/standards/ai-agents/durable-workflows-hitl.md +191 -0
  34. package/framework/standards/ai-agents/rag-custom-pgvector.md +246 -243
  35. package/framework/standards/ai-agents/setup.md +3 -0
  36. package/framework/standards/ai-agents/vector-data-extensions.md +170 -0
  37. package/framework/standards/ai-agents/workflows.md +190 -187
  38. package/framework/standards/infrastructure/docker/{easypanel-deploy.md → coolify-deploy.md} +49 -20
  39. package/framework/templates/README.md +7 -7
  40. package/framework/templates/REGISTRY.json +15 -17
  41. package/framework/templates/context/CONTEXT-FEATURE.md +10 -17
  42. package/framework/templates/context/CONTEXT.md +181 -180
  43. package/framework/templates/docs/onboarding.md +122 -118
  44. package/framework/templates/docs/proposal.md +12 -2
  45. package/framework/templates/docs/spec.md +228 -228
  46. package/framework/templates/frontend/nextjs/Dockerfile.nextjs.hbs +1 -1
  47. package/framework/templates/infrastructure/docker/coolify.template.json +49 -0
  48. package/framework/templates/infrastructure/github/README.md +6 -6
  49. package/framework/templates/infrastructure/github/workflows/deploy-coolify.yml.hbs +33 -0
  50. package/framework/templates/meta-prompts/parallel-workers/parallel-worker.md +80 -80
  51. package/framework/templates/meta-prompts/squad-leaders/backend-squad.md +90 -89
  52. package/framework/templates/meta-prompts/squad-leaders/frontend-squad.md +2 -1
  53. package/framework/templates/meta-prompts/squad-leaders/squad-leader.md +44 -43
  54. package/framework/templates/meta-prompts/validators/checkpoint-validator.md +107 -107
  55. package/package.json +1 -1
  56. package/src/commands/project/update.js +415 -368
  57. package/src/commands/project/worktree.js +212 -210
  58. package/src/commands/state/advance.js +82 -0
  59. package/src/commands/state/archive.js +111 -0
  60. package/src/commands/validation/validate-feature.js +229 -105
  61. package/src/core/state/state-manager.js +238 -66
  62. package/src/dashboard/scanner/state-scanner.js +35 -8
  63. package/src/lib/generators/context-generator.js +12 -8
  64. package/src/utils/file-copier.js +7 -0
  65. package/framework/templates/infrastructure/docker/easypanel.template.json +0 -46
  66. package/framework/templates/infrastructure/github/workflows/deploy-easypanel.yml.hbs +0 -31
@@ -4,7 +4,7 @@ description: Entende o negócio, os usuários e a arquitetura antes de qualquer
4
4
  version: core
5
5
  when-to-invoke: ao receber um novo request de feature, antes de morph-plan
6
6
  user-invokable: true
7
- cliVersion: "7.4.0"
7
+ cliVersion: "7.6.0"
8
8
  ---
9
9
 
10
10
  # morph-brainstorming
@@ -87,9 +87,9 @@ O harness conhece o stack por padrão. Não pergunte sobre tecnologia a menos qu
87
87
  const riskList = getRiskSignals(signals);
88
88
  ```
89
89
 
90
- **Persista a decisão em `state.json` ANTES de apresentar ou auto-aprovar** — grave
91
- `features[feature].gateDecisions.proposal = { pause, reason, signals, trust, decidedAt: <ISO> }`
92
- (Edit em `.morph/state.json` é permitido). O hook `gate-guard.js` confere depois se a ação (pausar vs auto-aprovar) bateu com esta decisão e avisa em divergência. Sem o registro o guard não verifica — não pule.
90
+ **Persista a decisão no `feature.json` autoritativo da feature ANTES de apresentar ou auto-aprovar** — grave
91
+ `gateDecisions.proposal = { pause, reason, signals, trust, decidedAt: <ISO> }` em `.morph/features/{feature}/feature.json`
92
+ (Edit em `.morph/features/**` é permitido pela allowlist). O hook `gate-guard.js` confere depois se a ação (pausar vs auto-aprovar) bateu com esta decisão e avisa em divergência. Sem o registro o guard não verifica — não pule.
93
93
 
94
94
  **Se `pause === true`** (manual mode OU risco detectado em auto), use `AskUserQuestion`:
95
95
 
@@ -116,7 +116,7 @@ O harness conhece o stack por padrão. Não pergunte sobre tecnologia a menos qu
116
116
  **Riscos:** {principal} (sinais detectados: nenhum)
117
117
  ```
118
118
 
119
- O hook state-sync.js detecta a aprovação automaticamente. Em modo auto sem AskUserQuestion, a skill atualiza `.morph/state.json` via Edit direto (allowlist permite — ver settings.local.json).
119
+ No caminho manual, o hook state-sync.js detecta a aprovação automaticamente (grava o gate no `feature.json` autoritativo, `approvedBy: "manual"`). Em modo auto sem AskUserQuestion, aprove via `morph-spec approve {feature} proposal --mode auto` (ou `morph-spec advance {feature}` para já criar a pasta 2-plan/) — nunca via Edit à mão de state.json.
120
120
 
121
121
  ## Output esperado
122
122
  - `.morph/features/{feature}/0-proposal/proposal.md` — proposta aprovada
@@ -4,7 +4,7 @@ description: Avalia a qualidade de uma tarefa ou fase com rubricas graduadas 0-1
4
4
  version: core
5
5
  when-to-invoke: ao completar uma tarefa ou ao finalizar uma fase de implementação
6
6
  user-invokable: false
7
- cliVersion: "7.4.0"
7
+ cliVersion: "7.6.0"
8
8
  ---
9
9
 
10
10
  # morph-eval
@@ -4,7 +4,7 @@ description: Guia a execução de cada tarefa de implementação com standard e
4
4
  version: core
5
5
  when-to-invoke: ao iniciar cada task de implementação listada em tasks.json
6
6
  user-invokable: false
7
- cliVersion: "7.4.0"
7
+ cliVersion: "7.6.0"
8
8
  ---
9
9
 
10
10
  # morph-implement
@@ -109,9 +109,10 @@ Ao iniciar cada task de `tasks.json` — antes de escrever qualquer código. Nor
109
109
 
110
110
  `tasks.json` é protegido após o Gate 2, mas os campos de progresso (`status`,
111
111
  `outputs`, `notes`) continuam graváveis durante a fase implement. `writeTasksJson`
112
- grava o arquivo **inteiro** (Write) — esse é o caminho permitido. Nunca use a
113
- tool `Edit` em `tasks.json` após o Gate 2: o hook `protect-spec-files` bloqueia
114
- Edit e qualquer mudança em campos de plano (`title`, `dependencies`, `doneCriteria`…).
112
+ grava o arquivo inteiro (Write) e é o caminho mais simples; **uma `Edit` que mexe
113
+ só nesses campos mutáveis também é permitida** — o hook `protect-spec-files` faz
114
+ diff semântico e libera Write ou Edit desde que nenhum campo de contrato do plano
115
+ (`title`, `description`, `dependencies`, `effort`, `doneCriteria`, `group`) mude.
115
116
 
116
117
  9. Quando invocada por `morph-apply`, retornar o controle à skill orquestradora — ela decide qual task vem em seguida via `getReadyTasks(data)`. Quando invocada manualmente, verificar se há próxima task ready e iniciá-la imediatamente aplicando esta skill desde o passo 1 (auto-advance). Pause apenas se encontrar ambiguidade bloqueante não coberta pelo mandate.
117
118
 
@@ -4,7 +4,7 @@ description: Guia a produção dos três artefatos de planejamento de uma featur
4
4
  version: core
5
5
  when-to-invoke: ao iniciar a fase plan de uma feature, após o Gate 1 (proposal aprovado)
6
6
  user-invokable: true
7
- cliVersion: "7.4.0"
7
+ cliVersion: "7.6.0"
8
8
  ---
9
9
 
10
10
  # morph-plan
@@ -94,9 +94,9 @@ Ao iniciar a fase plan — após o `proposal.md` ser aprovado e antes de qualque
94
94
  const { pause, reason } = shouldPauseGate(config, signals);
95
95
  ```
96
96
 
97
- **Persista a decisão em `state.json` ANTES de apresentar ou auto-aprovar** — grave
98
- `features[feature].gateDecisions.plan = { pause, reason, signals, trust, decidedAt: <ISO> }`
99
- (Edit em `.morph/state.json` é permitido). Isso deixa o raciocínio de trust auditável no disco: o hook `gate-guard.js` confere depois se a ação tomada (pausar vs auto-aprovar) bateu com esta decisão e avisa em caso de divergência. Sem este registro o guard não tem como verificar — não pule.
97
+ **Persista a decisão no `feature.json` autoritativo da feature ANTES de apresentar ou auto-aprovar** — grave
98
+ `gateDecisions.plan = { pause, reason, signals, trust, decidedAt: <ISO> }` em `.morph/features/{feature}/feature.json`
99
+ (Edit em `.morph/features/**` é permitido pela allowlist). Isso deixa o raciocínio de trust auditável no disco: o hook `gate-guard.js` confere depois se a ação tomada (pausar vs auto-aprovar) bateu com esta decisão e avisa em caso de divergência. Sem este registro o guard não tem como verificar — não pule.
100
100
 
101
101
  **Se `pause === true`**, use `AskUserQuestion`:
102
102
 
@@ -121,14 +121,14 @@ Ao iniciar a fase plan — após o `proposal.md` ser aprovado e antes de qualque
121
121
  **Tasks:** {N} | Esforço total: {soma} | Caminho crítico: {IDs sem deps paralelas}
122
122
  ```
123
123
 
124
- O hook state-sync registra a aprovação automaticamente em answers de AskUserQuestion. Em auto sem AskUserQuestion, a skill edita `state.json` diretamente.
124
+ No caminho manual, o hook state-sync registra a aprovação automaticamente em answers de AskUserQuestion (grava o gate no `feature.json` autoritativo, `approvedBy: "manual"`). Em auto sem AskUserQuestion, aprove via `morph-spec approve {feature} plan --mode auto` (ou `morph-spec advance {feature}` para já criar a pasta da próxima fase) — nunca via Edit à mão de state.json.
125
125
 
126
126
  ## Output esperado
127
127
 
128
128
  - `.morph/features/{feature}/2-plan/spec.md` — especificação técnica completa
129
129
  - `.morph/features/{feature}/2-plan/mandate.md` — contrato de implementação
130
130
  - `.morph/features/{feature}/2-plan/tasks.json` — task list estruturada válida contra schema
131
- - Gate 2 marcado aprovado em `state.json`
131
+ - Gate 2 registrado aprovado em `.morph/features/{feature}/feature.json` (autoritativo)
132
132
 
133
133
  ## Referências
134
134
 
@@ -4,7 +4,7 @@ description: Orquestra a fase de proposta+plano de uma feature morph-spec. Encad
4
4
  version: core
5
5
  when-to-invoke: ao receber instrução "use /morph-proposal" do usuário, ou ao iniciar uma nova feature
6
6
  user-invokable: true
7
- cliVersion: "7.4.0"
7
+ cliVersion: "7.6.0"
8
8
  ---
9
9
 
10
10
  # morph-proposal
@@ -17,7 +17,7 @@ Conduzir uma feature do request inicial até o plano aprovado (Gate 2), sem inte
17
17
 
18
18
  - Usuário pede "use /morph-proposal {feature}" ou "vamos planejar {feature}".
19
19
  - Início de qualquer feature nova num projeto morph-spec.
20
- - Em sessões retomadas, antes de continuar uma feature em progresso, verificar via `.morph/state.json` se a feature já passou pelos Gates.
20
+ - Em sessões retomadas, antes de continuar uma feature em progresso, verificar via `.morph/features/{feature-name}/feature.json` (estado autoritativo da feature) se ela já passou pelos Gates.
21
21
 
22
22
  ## Argumentos
23
23
 
@@ -82,11 +82,12 @@ Escopo: {incluído} | Excluído: {exclusões}
82
82
  Stack: {compliance}
83
83
  ```
84
84
 
85
- O hook `state-sync.js` registra a aprovação automaticamente via PostToolUse em AskUserQuestion. Quando auto-aprovado, marcar manualmente:
85
+ No caminho manual, o hook `state-sync.js` registra a aprovação automaticamente via PostToolUse quando a `AskUserQuestion` é respondida com "aprovado" (grava o gate no `feature.json` autoritativo, `approvedBy: "manual"`). Em `trust: auto` (sem AskUserQuestion), **aprove via CLI — nunca via Edit à mão de state.json**:
86
86
 
87
- ```js
88
- // auto-aprovação: o hook detecta "aprovado" no answer label, mas em auto-mode
89
- // não há AskUserQuestion. Skill atualiza .morph/state.json via Edit fallback.
87
+ ```
88
+ morph-spec approve {feature} proposal --mode auto
89
+ # ou, para já aprovar o Gate 1 e criar a pasta 2-plan/ num passo só:
90
+ morph-spec advance {feature}
90
91
  ```
91
92
 
92
93
  ### 4. Verificar retomada antes de morph-plan
@@ -128,7 +129,7 @@ Lógica idêntica ao passo 3 (`shouldPauseGate` + AskUserQuestion ou auto-aprova
128
129
  - `.morph/features/{feature}/2-plan/spec.md`
129
130
  - `.morph/features/{feature}/2-plan/mandate.md`
130
131
  - `.morph/features/{feature}/2-plan/tasks.json`
131
- - Gates 1 e 2 marcados aprovados em `.morph/state.json` (via hook state-sync).
132
+ - Gates 1 e 2 registrados aprovados em `.morph/features/{feature}/feature.json` (autoritativo) — via `morph-spec approve`/`morph-spec advance` em auto, ou via hook state-sync no caminho manual.
132
133
 
133
134
  ## Referências
134
135
 
@@ -4,7 +4,7 @@ description: Conduz a fase de revisão e produz o review-report.md para o Gate 3
4
4
  version: core
5
5
  when-to-invoke: ao iniciar a fase review, após todas as tasks de tasks.json estarem com status `done`
6
6
  user-invokable: true
7
- cliVersion: "7.4.0"
7
+ cliVersion: "7.6.0"
8
8
  ---
9
9
 
10
10
  # morph-review
@@ -88,12 +88,12 @@ e o `recap.md` estar atualizado. Normalmente invocada pela skill orquestradora `
88
88
  **Recomendação:** Aprovado
89
89
  ```
90
90
 
91
- O hook state-sync detecta a aprovação em answers de AskUserQuestion e marca `features[X].approvalGates.review = true`. Em auto sem AskUserQuestion, a skill atualiza state.json direto.
91
+ No caminho manual, o hook state-sync detecta a aprovação em answers de AskUserQuestion e grava `approvalGates.review = { approved: true, timestamp, approvedBy: "manual" }` no `feature.json` autoritativo da feature. Em auto sem AskUserQuestion, aprove via `morph-spec approve {feature} review --mode auto` — nunca via Edit à mão de state.json.
92
92
 
93
93
  ## Output esperado
94
94
  - `.morph/features/{feature}/4-review/review-report.md` com scores e recomendação
95
95
  - Trace da fase com `phase_summary` preenchido
96
- - Gate 3 marcado aprovado em state.json (auto ou manual)
96
+ - Gate 3 registrado aprovado em `.morph/features/{feature}/feature.json` (autoritativo), na forma canônica `{ approved, timestamp, approvedBy }` — via `morph-spec approve` em auto, ou pelo hook state-sync no caminho manual
97
97
 
98
98
  ## Referências
99
99
  - `.morph/framework/MORPH.md` — seção 5 (Gate 3), seção 1 (papel Evaluator), Trust Mode
@@ -4,7 +4,7 @@ description: Varre o codebase existente, detecta stack atual, compara com o stac
4
4
  version: core
5
5
  when-to-invoke: ao iniciar uma feature em projeto brownfield (codebase já existente)
6
6
  user-invokable: true
7
- cliVersion: "7.4.0"
7
+ cliVersion: "7.6.0"
8
8
  ---
9
9
 
10
10
  # morph-stack-scan
@@ -4,7 +4,7 @@ description: Localiza e carrega um standard específico do STANDARDS.json sob de
4
4
  version: core
5
5
  when-to-invoke: quando precisar consultar um standard específico durante a implementação
6
6
  user-invokable: true
7
- cliVersion: "7.4.0"
7
+ cliVersion: "7.6.0"
8
8
  ---
9
9
 
10
10
  # morph-standards
@@ -1495,9 +1495,9 @@
1495
1495
  "digest": "Vitest for unit tests, React Testing Library for component tests. Mock next/navigation with vi.mock. Test Server Actions with direct function calls. Use MSW for API mocking in component tests. Playwright for E2E tests against running app. Never test implementation details — test user behavior and output."
1496
1496
  },
1497
1497
  {
1498
- "id": "infrastructure-docker-easypanel-deploy",
1499
- "name": "easypanel deploy",
1500
- "path": "infrastructure/docker/easypanel-deploy.md",
1498
+ "id": "infrastructure-docker-coolify-deploy",
1499
+ "name": "coolify deploy",
1500
+ "path": "infrastructure/docker/coolify-deploy.md",
1501
1501
  "category": "infrastructure",
1502
1502
  "subcategory": "docker",
1503
1503
  "tags": [
@@ -1508,17 +1508,20 @@
1508
1508
  "docker"
1509
1509
  ],
1510
1510
  "aliases": [
1511
- "easypanel",
1511
+ "coolify",
1512
1512
  "docker deploy",
1513
1513
  "self-hosted",
1514
1514
  "VPS deployment",
1515
- "docker compose"
1515
+ "docker compose",
1516
+ "coolify api",
1517
+ "ssh deploy"
1516
1518
  ],
1517
1519
  "anchors": {
1518
- "setup": "#easypanel-service-config",
1519
- "deployment": "#zero-downtime-deploys"
1520
+ "setup": "#coolify-service-config",
1521
+ "deployment": "#zero-downtime-deploys",
1522
+ "ssh": "#triggering-a-deploy-via-ssh--api"
1520
1523
  },
1521
- "digest": "Create app in EasyPanel from container registry image. Configure environment variables in EasyPanel UI — not in Dockerfile. Use EasyPanel's managed Postgres/Redis for databases. Configure /health endpoint for health checks. Enable HTTPS via Let's Encrypt automatically. Use EasyPanel volumes for persistent storage."
1524
+ "digest": "Coolify is the self-hosted Docker PaaS. No MCP/CLI exists — all access is via SSH into the VPS, hitting the Coolify API at localhost:8000/api/v1 with a server-side token (never printed, never leaves the SSH session). Configure environment variables in Coolify UI/API — not in Dockerfile. Configure /health endpoint for health checks. HTTPS via Let's Encrypt automatic (Traefik). Redeploys trigger via GET /deploy?uuid={uuid} over SSH."
1522
1525
  },
1523
1526
  {
1524
1527
  "id": "infrastructure-neon-setup",
@@ -1657,6 +1660,75 @@
1657
1660
  "edit-workflows": "#iterative-editing-workflows"
1658
1661
  },
1659
1662
  "digest": "Guidelines for generating project images with Gemini Image MCP — prompt patterns, aspect ratios, design-system-aware prompts, iterative editing, and assets.md format."
1663
+ },
1664
+ {
1665
+ "id": "ai-agents-durable-workflows-hitl",
1666
+ "name": "durable workflows hitl",
1667
+ "path": "ai-agents/durable-workflows-hitl.md",
1668
+ "category": "ai-agents",
1669
+ "subcategory": null,
1670
+ "tags": [
1671
+ "ai-agents",
1672
+ "maf",
1673
+ "workflow",
1674
+ "durability"
1675
+ ],
1676
+ "stacks": [
1677
+ "dotnet"
1678
+ ],
1679
+ "aliases": [
1680
+ "durable workflow",
1681
+ "checkpoint",
1682
+ "checkpointing",
1683
+ "human in the loop",
1684
+ "hitl",
1685
+ "requestport",
1686
+ "requestinfoevent",
1687
+ "resume workflow",
1688
+ "long running workflow",
1689
+ "approval workflow",
1690
+ "icheckpointstore"
1691
+ ],
1692
+ "anchors": {
1693
+ "when": "#quando-usar-este-standard",
1694
+ "hitl": "#human-in-the-loop-via-requestport",
1695
+ "checkpointing": "#checkpointing--durável-sem-depender-de-azure",
1696
+ "durable-task": "#azure-durable-task-extension--escape-hatch-not-default"
1697
+ },
1698
+ "digest": "HITL via RequestPort — a workflow node that pauses and waits for an external (human) response via RequestInfoEvent. Checkpointing is GA and Azure-free: CheckpointManager.CreateInMemory() for tests, CreateJson(FileSystemJsonCheckpointStore) for local-disk durability, or a custom ICheckpointStore (recommended against the project's own Postgres). Microsoft.Agents.AI.DurableTask (Azure Durable Task Scheduler) is documented as an escape hatch only, not adopted as default."
1699
+ },
1700
+ {
1701
+ "id": "ai-agents-vector-data-extensions",
1702
+ "name": "vector data extensions",
1703
+ "path": "ai-agents/vector-data-extensions.md",
1704
+ "category": "ai-agents",
1705
+ "subcategory": null,
1706
+ "tags": [
1707
+ "ai-agents",
1708
+ "maf",
1709
+ "rag",
1710
+ "vector-data"
1711
+ ],
1712
+ "stacks": [
1713
+ "dotnet"
1714
+ ],
1715
+ "aliases": [
1716
+ "microsoft extensions vectordata",
1717
+ "vectorstorecollection",
1718
+ "vector store abstraction",
1719
+ "data ingestion",
1720
+ "ingestion pipeline",
1721
+ "semantic kernel connectors exception",
1722
+ "pgvector connector",
1723
+ "vectorstorewriter"
1724
+ ],
1725
+ "anchors": {
1726
+ "naming-trap": "#a-pegadinha-de-nomenclatura-crítico--leia-antes-de-tudo",
1727
+ "maturity": "#maturidade-atual-verificado-2026-07-06",
1728
+ "decision-matrix": "#decision-matrix--custom-pgvector-vs-vectordata",
1729
+ "data-ingestion": "#microsoftextensionsdataingestion-preview--pipeline-pattern-para-adoção-futura"
1730
+ },
1731
+ "digest": "Microsoft.Extensions.VectorData.Abstractions (GA) is the provider-agnostic vector-store abstraction; Microsoft.Extensions.DataIngestion (preview) is the companion ETL pipeline. Concrete connectors (Postgres, Qdrant, SQL Server...) ship under Microsoft.SemanticKernel.Connectors.* package names despite having nothing to do with Semantic Kernel — an explicit exception to ai-agents-setup's SK ban. Default RAG storage stays ai-agents-rag-custom-pgvector; adopt VectorData only for genuine multi-backend portability, since the Postgres connector is still prerelease."
1660
1732
  }
1661
1733
  ],
1662
1734
  "sharedAliases": {
@@ -0,0 +1,191 @@
1
+ # MAF Workflows — Checkpointing & Human-in-the-Loop
2
+
3
+ > **Scope:** stacks=["dotnet"]
4
+ > **Layer:** 1 (on-keyword)
5
+ > **Keywords:** durable workflow, checkpoint, checkpointing, human in the loop, hitl, requestport, requestinfoevent, resume workflow, long running workflow, approval workflow, icheckpointstore, filesystemjsoncheckpointstore
6
+ > **Read by Claude in:** plan (when `ai-agents-workflows` identifies a genuine HITL or long-running need) + implement (wiring `RequestPort` / `CheckpointManager`)
7
+
8
+ **Verified against:** Microsoft.Agents.AI 1.0 GA (`Microsoft.Agents.AI.Workflows` namespace) + Microsoft.Agents.AI.DurableTask (prerelease, Azure-coupled — documented as an escape hatch, not adopted as default). Last-verified: 2026-07-06.
9
+
10
+ ---
11
+
12
+ ## Quando usar este standard
13
+
14
+ Read `ai-agents-sweet-spot` and `ai-agents-workflows` first. This standard applies only once `ai-agents-workflows` has justified a Workflow **and** the reason is one of:
15
+
16
+ - Long-running execution with a human-in-the-loop (HITL) approval step.
17
+ - A workflow whose state must survive a process restart (deploy, crash, restart) between steps.
18
+
19
+ If neither applies, a plain workflow (or no workflow at all — see `ai-agents-sweet-spot`) is enough; do not add checkpointing or `RequestPort` "just in case."
20
+
21
+ ---
22
+
23
+ ## Human-in-the-Loop via `RequestPort`
24
+
25
+ HITL is modeled with `RequestPort` — a special node in the workflow graph that pauses execution and waits for an external (human) response instead of processing automatically.
26
+
27
+ ```csharp
28
+ using Microsoft.Agents.AI.Workflows;
29
+
30
+ // Request type the port sends out, response type it expects back.
31
+ RequestPort<ApprovalRequest, bool> approvalPort =
32
+ RequestPort.Create<ApprovalRequest, bool>("RefundApproval");
33
+
34
+ var workflow = new WorkflowBuilder(refundAnalyzerAgent)
35
+ .AddEdge(refundAnalyzerAgent, approvalPort)
36
+ .AddEdge(approvalPort, refundProcessorExecutor)
37
+ .WithOutputFrom(refundProcessorExecutor)
38
+ .Build();
39
+ ```
40
+
41
+ Running the workflow, the caller watches for `RequestInfoEvent` and sends the human's answer back — the framework routes the response to the executor that made the request:
42
+
43
+ ```csharp
44
+ await using StreamingRun run = await InProcessExecution.RunStreamingAsync(workflow, input);
45
+ await foreach (WorkflowEvent evt in run.WatchStreamAsync())
46
+ {
47
+ switch (evt)
48
+ {
49
+ case RequestInfoEvent requestEvt:
50
+ // Surface requestEvt.Request to a human (dashboard, email, Slack, ...).
51
+ // This is where execution pauses until an external answer arrives.
52
+ bool approved = await GetHumanDecisionAsync(requestEvt.Request);
53
+ await run.SendResponseAsync(requestEvt.Request.CreateResponse(approved));
54
+ break;
55
+
56
+ case WorkflowOutputEvent outputEvt:
57
+ Console.WriteLine($"Workflow completed: {outputEvt.Data}");
58
+ break;
59
+ }
60
+ }
61
+ ```
62
+
63
+ **Agent tool-approval uses the same mechanism.** When an agent orchestration (sequential, concurrent, group chat) calls a tool marked as requiring approval, the workflow pauses and emits a `RequestInfoEvent` whose payload is a `ToolApprovalRequestContent` instead of a custom request type — no separate API to learn. See `ai-agents-multi-agent-patterns` for agent orchestration patterns this composes with.
64
+
65
+ ---
66
+
67
+ ## Checkpointing — durável sem depender de Azure
68
+
69
+ Checkpointing is part of the **base, GA** `Microsoft.Agents.AI.Workflows` namespace — no extra/preview package required. A checkpoint captures the full workflow state at each superstep boundary: executor state, pending messages, and pending `RequestPort` requests (so a pending approval survives a restore).
70
+
71
+ ### Three storage options
72
+
73
+ | Store | Durability | When to use |
74
+ |-------|-----------|-------------|
75
+ | `CheckpointManager.CreateInMemory()` | In-process only, lost on restart | Tests, demos, short-lived workflows |
76
+ | `CheckpointManager.CreateJson(new FileSystemJsonCheckpointStore(dir))` | Local disk, survives process restart | Single-instance VPS deployments — no Azure, no preview package |
77
+ | Custom `ICheckpointStore` implementation | Whatever backend you implement it against | **Recommended for production** — implement against the project's own Neon Postgres table, matching the ownership model already used by `ai-agents-rag-custom-pgvector` |
78
+
79
+ ```csharp
80
+ using Microsoft.Agents.AI.Workflows;
81
+
82
+ // Local-disk durability — no Azure dependency:
83
+ var checkpointFolder = Directory.CreateDirectory("./checkpoints");
84
+ CheckpointManager checkpointManager = CheckpointManager.CreateJson(
85
+ new FileSystemJsonCheckpointStore(checkpointFolder));
86
+
87
+ StreamingRun run = await InProcessExecution.RunStreamingAsync(workflow, input, checkpointManager);
88
+ await foreach (WorkflowEvent evt in run.WatchStreamAsync())
89
+ {
90
+ if (evt is SuperStepCompletedEvent stepEvt)
91
+ {
92
+ CheckpointInfo? checkpoint = stepEvt.CompletionInfo?.Checkpoint;
93
+ // Persist checkpoint.CheckpointId alongside the business record
94
+ // (e.g. the refund request row) so it can be resumed after a restart.
95
+ }
96
+ }
97
+ ```
98
+
99
+ Resuming after a restart — rehydrate into a **new** run instance from the last known checkpoint:
100
+
101
+ ```csharp
102
+ StreamingRun resumedRun = await InProcessExecution.ResumeStreamingAsync(
103
+ newWorkflowInstance, savedCheckpoint, checkpointManager);
104
+
105
+ await foreach (WorkflowEvent evt in resumedRun.WatchStreamAsync())
106
+ {
107
+ // A pending RequestPort request is re-emitted here as a RequestInfoEvent —
108
+ // the paused approval survives the restart.
109
+ if (evt is RequestInfoEvent requestEvt) { /* re-surface to the human */ }
110
+ }
111
+ ```
112
+
113
+ ### Custom executor state
114
+
115
+ An executor with instance state beyond the message flow must opt in explicitly:
116
+
117
+ ```csharp
118
+ internal sealed class RefundAnalyzerExecutor() : Executor("RefundAnalyzer")
119
+ {
120
+ private const string StateKey = "AnalysisHistory";
121
+ private List<string> _history = [];
122
+
123
+ protected override ValueTask OnCheckpointingAsync(IWorkflowContext context, CancellationToken ct = default)
124
+ => context.QueueStateUpdateAsync(StateKey, _history);
125
+
126
+ protected override async ValueTask OnCheckpointRestoredAsync(IWorkflowContext context, CancellationToken ct = default)
127
+ => _history = await context.ReadStateAsync<List<string>>(StateKey);
128
+ }
129
+ ```
130
+
131
+ ### `ICheckpointStore` — Neon/Postgres-backed (recommended production pattern)
132
+
133
+ Implementing `ICheckpointStore` against the project's own Postgres database keeps checkpoint durability inside the same infrastructure the rest of the project already trusts (see `infrastructure-neon-setup`), instead of introducing a new storage dependency:
134
+
135
+ ```csharp
136
+ public sealed class PostgresCheckpointStore(NpgsqlDataSource dataSource) : ICheckpointStore
137
+ {
138
+ // Persist/retrieve checkpoint blobs in a workflow_checkpoints table
139
+ // (workflow_id, checkpoint_id, payload jsonb/bytea, created_at).
140
+ // Implement per the ICheckpointStore contract — see Microsoft.Agents.AI.Workflows docs.
141
+ }
142
+ ```
143
+
144
+ > **Security:** checkpoint storage is a trust boundary. Never restore a checkpoint from an untrusted or unauthenticated source — a tampered checkpoint can inject arbitrary workflow state.
145
+
146
+ ---
147
+
148
+ ## Azure Durable Task extension — escape hatch, not default
149
+
150
+ `Microsoft.Agents.AI.DurableTask` (prerelease) adds durability backed by the **Azure Durable Task Scheduler**, including cost-efficient multi-day HITL waits with zero compute cost while paused. Even its "bring-your-own-compute / self-hosted" mode still requires a Durable Task Scheduler backend (`Microsoft.DurableTask.*.AzureManaged` packages) — self-hosted means your own worker process, not an Azure-free backend.
151
+
152
+ This framework deploys to VPS/Coolify by default and does not adopt Azure as a default dependency (see `ai-agents-setup` §3.4 on Azure OpenAI). For that reason, `Microsoft.Agents.AI.DurableTask` is **not adopted as default** here. The `CheckpointManager` + custom `ICheckpointStore` pattern above covers the same durability need without an Azure dependency. Only reach for the Durable Task extension when a specific client already runs on Azure and needs multi-day/serverless HITL waits — record that decision in `decisions.md`.
153
+
154
+ ---
155
+
156
+ ## Anti-patterns
157
+
158
+ | Anti-pattern | Why it's wrong | Right way |
159
+ |--------------|----------------|-----------|
160
+ | Adding a `Workflow` with `RequestPort` but no `CheckpointManager` | A pending human approval is lost if the process restarts while waiting | Wire a persistent `CheckpointManager` (file-based or custom `ICheckpointStore`) whenever a HITL wait can outlive a single process lifetime |
161
+ | Reaching for `Microsoft.Agents.AI.DurableTask` by default | Adds an Azure Durable Task Scheduler dependency this framework's VPS-first stack doesn't otherwise need | Use base `CheckpointManager` + custom `ICheckpointStore`; reserve the Durable Task extension for projects already committed to Azure |
162
+ | Storing checkpoints in a public or unauthenticated location | Checkpoint storage is a trust boundary — a tampered checkpoint can inject arbitrary state on restore | Restrict read/write access to the checkpoint store the same way you would a secrets store |
163
+ | Skipping `OnCheckpointingAsync`/`OnCheckpointRestoredAsync` on an executor with instance state | State silently resets to defaults after a resume, corrupting workflow logic | Any executor with fields beyond the message flow must implement both methods |
164
+
165
+ ---
166
+
167
+ ## Checklist (verifiable by morph-eval)
168
+
169
+ - [ ] `ai-agents-workflows`' HITL or long-running criterion is documented in `decisions.md` before this standard is applied.
170
+ - [ ] Every HITL wait point uses `RequestPort` + handles `RequestInfoEvent`.
171
+ - [ ] A `CheckpointManager` is wired whenever the workflow can pause across a process restart — `CreateInMemory()` is acceptable only for tests/demos.
172
+ - [ ] If a custom `ICheckpointStore` was implemented, it persists to infrastructure already owned by the project (e.g., Neon Postgres), not a new ad hoc store.
173
+ - [ ] Executors with instance state implement `OnCheckpointingAsync` and `OnCheckpointRestoredAsync`.
174
+ - [ ] `Microsoft.Agents.AI.DurableTask` is present only if the project already runs on Azure and the choice is recorded in `decisions.md`.
175
+
176
+ ---
177
+
178
+ ## References
179
+
180
+ - `ai-agents-workflows` — when a Workflow (and therefore this standard) is justified
181
+ - `ai-agents-sweet-spot` — single agent vs workflow decision
182
+ - `ai-agents-multi-agent-patterns` — agent orchestrations that share the tool-approval HITL mechanism
183
+ - `ai-agents-setup` — the VPS-first, non-Azure-default stance this standard follows for durability
184
+ - `infrastructure-neon-setup` — Postgres connection patterns for a custom `ICheckpointStore`
185
+ - Human-in-the-loop (HITL) | Microsoft Learn: https://learn.microsoft.com/en-us/agent-framework/workflows/human-in-the-loop
186
+ - Checkpoints | Microsoft Learn: https://learn.microsoft.com/en-us/agent-framework/workflows/checkpoints
187
+ - Durable Extension | Microsoft Learn: https://learn.microsoft.com/en-us/agent-framework/integrations/durable-extension
188
+
189
+ ---
190
+
191
+ *MORPH-SPEC by Polymorphism Tech — ai-agents/durable-workflows-hitl.md v1.0 (2026-07-06)*