@aperant/framework 0.10.0 → 0.12.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 (199) hide show
  1. package/CHANGELOG.md +236 -1
  2. package/agents/apt-planner.md +15 -0
  3. package/agents/apt-verifier.md +15 -10
  4. package/bin/apt-proof-video.mjs +256 -76
  5. package/dist/cli/commands/apply-recipe.d.mts +6 -0
  6. package/dist/cli/commands/apply-recipe.d.mts.map +1 -0
  7. package/dist/cli/commands/apply-recipe.mjs +98 -0
  8. package/dist/cli/commands/apply-recipe.mjs.map +1 -0
  9. package/dist/cli/commands/catch-up.d.mts +9 -0
  10. package/dist/cli/commands/catch-up.d.mts.map +1 -0
  11. package/dist/cli/commands/catch-up.mjs +64 -0
  12. package/dist/cli/commands/catch-up.mjs.map +1 -0
  13. package/dist/cli/commands/mobile-prepare.d.mts +53 -0
  14. package/dist/cli/commands/mobile-prepare.d.mts.map +1 -0
  15. package/dist/cli/commands/mobile-prepare.mjs +139 -0
  16. package/dist/cli/commands/mobile-prepare.mjs.map +1 -0
  17. package/dist/cli/commands/pr-review.d.mts +16 -4
  18. package/dist/cli/commands/pr-review.d.mts.map +1 -1
  19. package/dist/cli/commands/pr-review.mjs +30 -11
  20. package/dist/cli/commands/pr-review.mjs.map +1 -1
  21. package/dist/cli/commands/recipe-diff.d.mts +6 -0
  22. package/dist/cli/commands/recipe-diff.d.mts.map +1 -0
  23. package/dist/cli/commands/recipe-diff.mjs +75 -0
  24. package/dist/cli/commands/recipe-diff.mjs.map +1 -0
  25. package/dist/cli/commands/roadmap.d.mts.map +1 -1
  26. package/dist/cli/commands/roadmap.mjs +53 -0
  27. package/dist/cli/commands/roadmap.mjs.map +1 -1
  28. package/dist/cli/commands/route.d.mts.map +1 -1
  29. package/dist/cli/commands/route.mjs +92 -2
  30. package/dist/cli/commands/route.mjs.map +1 -1
  31. package/dist/cli/commands/verify-sandbox.d.mts +6 -0
  32. package/dist/cli/commands/verify-sandbox.d.mts.map +1 -0
  33. package/dist/cli/commands/verify-sandbox.mjs +200 -0
  34. package/dist/cli/commands/verify-sandbox.mjs.map +1 -0
  35. package/dist/cli/config/share-policy.d.mts.map +1 -1
  36. package/dist/cli/config/share-policy.mjs +1 -0
  37. package/dist/cli/config/share-policy.mjs.map +1 -1
  38. package/dist/cli/config/upgrade-gitignore.d.mts.map +1 -1
  39. package/dist/cli/config/upgrade-gitignore.mjs +1 -0
  40. package/dist/cli/config/upgrade-gitignore.mjs.map +1 -1
  41. package/dist/cli/consistency/parse-qa.d.mts.map +1 -1
  42. package/dist/cli/consistency/parse-qa.mjs +18 -4
  43. package/dist/cli/consistency/parse-qa.mjs.map +1 -1
  44. package/dist/cli/consistency/rules/r5-verdict-consistency.d.mts.map +1 -1
  45. package/dist/cli/consistency/rules/r5-verdict-consistency.mjs +11 -10
  46. package/dist/cli/consistency/rules/r5-verdict-consistency.mjs.map +1 -1
  47. package/dist/cli/coordination/catch-up.d.mts +26 -0
  48. package/dist/cli/coordination/catch-up.d.mts.map +1 -0
  49. package/dist/cli/coordination/catch-up.mjs +239 -0
  50. package/dist/cli/coordination/catch-up.mjs.map +1 -0
  51. package/dist/cli/coordination/last-seen.d.mts +45 -0
  52. package/dist/cli/coordination/last-seen.d.mts.map +1 -0
  53. package/dist/cli/coordination/last-seen.mjs +128 -0
  54. package/dist/cli/coordination/last-seen.mjs.map +1 -0
  55. package/dist/cli/coordination/store.d.mts +15 -0
  56. package/dist/cli/coordination/store.d.mts.map +1 -1
  57. package/dist/cli/coordination/store.mjs +16 -0
  58. package/dist/cli/coordination/store.mjs.map +1 -1
  59. package/dist/cli/coverage-check/user-outcomes.d.mts +7 -2
  60. package/dist/cli/coverage-check/user-outcomes.d.mts.map +1 -1
  61. package/dist/cli/coverage-check/user-outcomes.mjs +50 -17
  62. package/dist/cli/coverage-check/user-outcomes.mjs.map +1 -1
  63. package/dist/cli/dispatch.d.mts.map +1 -1
  64. package/dist/cli/dispatch.mjs +30 -0
  65. package/dist/cli/dispatch.mjs.map +1 -1
  66. package/dist/cli/gate/gates/verify-approved.d.mts +0 -48
  67. package/dist/cli/gate/gates/verify-approved.d.mts.map +1 -1
  68. package/dist/cli/gate/gates/verify-approved.mjs +23 -80
  69. package/dist/cli/gate/gates/verify-approved.mjs.map +1 -1
  70. package/dist/cli/git/default-branch.d.mts +51 -0
  71. package/dist/cli/git/default-branch.d.mts.map +1 -0
  72. package/dist/cli/git/default-branch.mjs +234 -0
  73. package/dist/cli/git/default-branch.mjs.map +1 -0
  74. package/dist/cli/git/identity.d.mts +3 -5
  75. package/dist/cli/git/identity.d.mts.map +1 -1
  76. package/dist/cli/git/identity.mjs +10 -4
  77. package/dist/cli/git/identity.mjs.map +1 -1
  78. package/dist/cli/install/legacy-paths.d.mts.map +1 -1
  79. package/dist/cli/install/legacy-paths.mjs +2 -0
  80. package/dist/cli/install/legacy-paths.mjs.map +1 -1
  81. package/dist/cli/install/mcp-server-specs.d.mts +6 -5
  82. package/dist/cli/install/mcp-server-specs.d.mts.map +1 -1
  83. package/dist/cli/install/mcp-server-specs.mjs +22 -4
  84. package/dist/cli/install/mcp-server-specs.mjs.map +1 -1
  85. package/dist/cli/roadmap/backend-adapter.d.mts +43 -0
  86. package/dist/cli/roadmap/backend-adapter.d.mts.map +1 -0
  87. package/dist/cli/roadmap/backend-adapter.mjs +222 -0
  88. package/dist/cli/roadmap/backend-adapter.mjs.map +1 -0
  89. package/dist/cli/util/runtime-capabilities.d.mts +7 -0
  90. package/dist/cli/util/runtime-capabilities.d.mts.map +1 -1
  91. package/dist/cli/util/runtime-capabilities.mjs +65 -0
  92. package/dist/cli/util/runtime-capabilities.mjs.map +1 -1
  93. package/dist/cli/verify-proof/manifest-validator.mjs +13 -0
  94. package/dist/cli/verify-proof/manifest-validator.mjs.map +1 -1
  95. package/dist/cli/verify-proof/resolver.d.mts +10 -7
  96. package/dist/cli/verify-proof/resolver.d.mts.map +1 -1
  97. package/dist/cli/verify-proof/resolver.mjs +28 -7
  98. package/dist/cli/verify-proof/resolver.mjs.map +1 -1
  99. package/dist/cli/verify-proof/sandbox/apply.d.mts +64 -0
  100. package/dist/cli/verify-proof/sandbox/apply.d.mts.map +1 -0
  101. package/dist/cli/verify-proof/sandbox/apply.mjs +379 -0
  102. package/dist/cli/verify-proof/sandbox/apply.mjs.map +1 -0
  103. package/dist/cli/verify-proof/sandbox/gate-predicate.d.mts +63 -0
  104. package/dist/cli/verify-proof/sandbox/gate-predicate.d.mts.map +1 -0
  105. package/dist/cli/verify-proof/sandbox/gate-predicate.mjs +115 -0
  106. package/dist/cli/verify-proof/sandbox/gate-predicate.mjs.map +1 -0
  107. package/dist/cli/verify-proof/sandbox/pattern-matcher.d.mts +57 -0
  108. package/dist/cli/verify-proof/sandbox/pattern-matcher.d.mts.map +1 -0
  109. package/dist/cli/verify-proof/sandbox/pattern-matcher.mjs +124 -0
  110. package/dist/cli/verify-proof/sandbox/pattern-matcher.mjs.map +1 -0
  111. package/dist/cli/verify-proof/sandbox/recipe-diff.d.mts +28 -0
  112. package/dist/cli/verify-proof/sandbox/recipe-diff.d.mts.map +1 -0
  113. package/dist/cli/verify-proof/sandbox/recipe-diff.mjs +190 -0
  114. package/dist/cli/verify-proof/sandbox/recipe-diff.mjs.map +1 -0
  115. package/dist/cli/verify-proof/sandbox/sandbox-config.d.mts +25 -0
  116. package/dist/cli/verify-proof/sandbox/sandbox-config.d.mts.map +1 -0
  117. package/dist/cli/verify-proof/sandbox/sandbox-config.mjs +85 -0
  118. package/dist/cli/verify-proof/sandbox/sandbox-config.mjs.map +1 -0
  119. package/dist/cli/verify-proof/sandbox/self-test.d.mts +39 -0
  120. package/dist/cli/verify-proof/sandbox/self-test.d.mts.map +1 -0
  121. package/dist/cli/verify-proof/sandbox/self-test.mjs +248 -0
  122. package/dist/cli/verify-proof/sandbox/self-test.mjs.map +1 -0
  123. package/dist/plugin/.claude-plugin/plugin.json +2 -1
  124. package/dist/plugin/agents/apt-planner.md +15 -0
  125. package/dist/plugin/agents/apt-verifier.md +15 -10
  126. package/dist/plugin/skills/apt/SKILL.md +29 -0
  127. package/dist/plugin/skills/apt-catch-up/SKILL.md +79 -0
  128. package/dist/plugin/skills/apt-plan/SKILL.md +23 -0
  129. package/dist/plugin/skills/apt-pr-review/SKILL.md +5 -4
  130. package/dist/plugin/skills/apt-run/SKILL.md +1 -1
  131. package/dist/plugin/skills/apt-setup/SKILL.md +132 -0
  132. package/dist/plugin/skills/apt-verify/SKILL.md +20 -15
  133. package/dist/plugin/skills/apt-verify-proof/SKILL.md +146 -11
  134. package/dist/proof-report.d.ts.map +1 -1
  135. package/dist/proof-report.js +4 -1
  136. package/dist/proof-report.js.map +1 -1
  137. package/dist/types/config.d.ts +42 -4
  138. package/dist/types/config.d.ts.map +1 -1
  139. package/dist/types/qa-scoring.d.ts +45 -9
  140. package/dist/types/qa-scoring.d.ts.map +1 -1
  141. package/dist/types/qa-scoring.js +34 -13
  142. package/dist/types/qa-scoring.js.map +1 -1
  143. package/dist/types/state.d.ts +7 -2
  144. package/dist/types/state.d.ts.map +1 -1
  145. package/dist/types/task-record.d.ts +10 -2
  146. package/dist/types/task-record.d.ts.map +1 -1
  147. package/drivers/mobile/README.md +40 -0
  148. package/drivers/mobile/driver.mjs +106 -0
  149. package/drivers/mobile/manifest.json +49 -0
  150. package/package.json +1 -1
  151. package/prompts/inbox-clarification.md +11 -0
  152. package/prompts/inbox-triage.md +20 -0
  153. package/prompts/qa_orchestrator_agentic.md +18 -17
  154. package/prompts/qa_reviewer.md +17 -15
  155. package/skills/apt/SKILL.md +29 -0
  156. package/skills/apt-catch-up/SKILL.md +79 -0
  157. package/skills/apt-plan/SKILL.md +23 -0
  158. package/skills/apt-pr-review/SKILL.md +5 -4
  159. package/skills/apt-run/SKILL.md +1 -1
  160. package/skills/apt-setup/SKILL.md +132 -0
  161. package/skills/apt-verifier.md +7 -4
  162. package/skills/apt-verify/SKILL.md +20 -15
  163. package/skills/apt-verify-proof/SKILL.md +146 -11
  164. package/src/cli/commands/apply-recipe.mjs +105 -0
  165. package/src/cli/commands/catch-up.mjs +67 -0
  166. package/src/cli/commands/mobile-prepare.mjs +151 -0
  167. package/src/cli/commands/pr-review.mjs +32 -11
  168. package/src/cli/commands/recipe-diff.mjs +81 -0
  169. package/src/cli/commands/roadmap.mjs +54 -0
  170. package/src/cli/commands/route.mjs +92 -1
  171. package/src/cli/commands/verify-sandbox.mjs +231 -0
  172. package/src/cli/config/share-policy.mjs +1 -0
  173. package/src/cli/config/upgrade-gitignore.mjs +1 -0
  174. package/src/cli/consistency/parse-qa.mjs +20 -4
  175. package/src/cli/consistency/rules/r5-verdict-consistency.mjs +11 -13
  176. package/src/cli/coordination/catch-up.mjs +231 -0
  177. package/src/cli/coordination/last-seen.mjs +131 -0
  178. package/src/cli/coordination/store.mjs +18 -0
  179. package/src/cli/coverage-check/user-outcomes.mjs +52 -17
  180. package/src/cli/dispatch.mjs +29 -0
  181. package/src/cli/gate/gates/verify-approved.mjs +22 -81
  182. package/src/cli/git/default-branch.mjs +250 -0
  183. package/src/cli/git/identity.mjs +9 -3
  184. package/src/cli/install/legacy-paths.mjs +2 -0
  185. package/src/cli/install/mcp-server-specs.mjs +24 -4
  186. package/src/cli/roadmap/backend-adapter.mjs +231 -0
  187. package/src/cli/util/runtime-capabilities.mjs +67 -0
  188. package/src/cli/verify-proof/manifest-validator.mjs +15 -0
  189. package/src/cli/verify-proof/resolver.mjs +27 -7
  190. package/src/cli/verify-proof/sandbox/apply.mjs +401 -0
  191. package/src/cli/verify-proof/sandbox/gate-predicate.mjs +126 -0
  192. package/src/cli/verify-proof/sandbox/pattern-matcher.mjs +127 -0
  193. package/src/cli/verify-proof/sandbox/recipe-diff.mjs +208 -0
  194. package/src/cli/verify-proof/sandbox/sandbox-config.mjs +82 -0
  195. package/src/cli/verify-proof/sandbox/sandbox-schema.json +33 -0
  196. package/src/cli/verify-proof/sandbox/self-test.mjs +265 -0
  197. package/templates/config.json +21 -1
  198. package/templates/proof-verification.md +27 -8
  199. package/workflows/verify-proof.md +376 -302
@@ -7,29 +7,43 @@ export interface QADimensionScore {
7
7
  score: number;
8
8
  rationale: string;
9
9
  }
10
- /** Threshold constants for QA verdict derivation */
10
+ /**
11
+ * Threshold constants for QA verdict derivation.
12
+ *
13
+ * v0.9.0 (verify-proof-v2-outcome-walker_27-05-26) — `approved_with_notes`
14
+ * is DELETED. Dimensions are now informational rationale only; the verdict
15
+ * derives from the two-gate envelope (`automation_gate` + `proof_gate`),
16
+ * not per-dimension minimums.
17
+ */
11
18
  export declare const QA_DIMENSION_THRESHOLDS: {
12
19
  readonly approved: 7;
13
- readonly approved_with_notes: 5;
14
20
  };
15
- /** Scored QA signoff replaces boolean approved/rejected with dimension scores */
21
+ /** Closed set of QA verdicts in v0.9.0+. `approved_with_notes` is deleted. */
22
+ export type QAVerdict = 'approved' | 'rejected' | 'needs_human';
23
+ /** The two boolean gates that drive verdict derivation. */
24
+ export type GateStatus = 'pass' | 'fail';
25
+ /** Scored QA signoff — verdict + checks + dimensions + findings */
16
26
  export interface ScoredQASignoff {
17
- status: 'approved' | 'approved-with-notes' | 'rejected';
27
+ status: QAVerdict;
18
28
  timestamp: string;
19
29
  iterations: number;
20
30
  dimensions: QADimensionScore[];
31
+ gates: {
32
+ automation_gate: GateStatus;
33
+ proof_gate: GateStatus;
34
+ };
21
35
  checks: {
22
36
  tests: {
23
- status: 'pass' | 'fail';
37
+ status: GateStatus;
24
38
  count: number;
25
39
  failures: string[];
26
40
  };
27
41
  typecheck: {
28
- status: 'pass' | 'fail';
42
+ status: GateStatus;
29
43
  errors: string[];
30
44
  };
31
45
  lint: {
32
- status: 'pass' | 'fail';
46
+ status: GateStatus;
33
47
  warnings: number;
34
48
  };
35
49
  };
@@ -40,6 +54,28 @@ export interface ScoredQASignoff {
40
54
  }>;
41
55
  notes: string;
42
56
  }
43
- /** Derive verdict from dimension scores */
44
- export declare function deriveVerdict(scores: QADimensionScore[]): ScoredQASignoff['status'];
57
+ /**
58
+ * Derive the v0.9.0 two-gate verdict from `{automation_gate, proof_gate}`.
59
+ *
60
+ * The legacy per-dimension `deriveVerdict(scores: QADimensionScore[])` is
61
+ * deleted along with `approved_with_notes` — dimensions are now informational
62
+ * rationale on the report.
63
+ *
64
+ * Rules:
65
+ * - `automation_gate: fail` → `rejected` (regardless of `proof_gate`).
66
+ * - `proof_gate: fail` → `needs_human` (signals UI-surface outcomes
67
+ * without proof) when automation is green; this aligns with the
68
+ * workflow-level rule "any test with needs_human_with_transport rolls
69
+ * up to needs_human" (callers that detect a hard fail without a
70
+ * transport hole should pass `proof_gate: 'fail'`).
71
+ * - Both gates pass → `approved`.
72
+ *
73
+ * Per AC13 (spec.md §3): the workflow's Step 7c rolls a `proof_gate: fail`
74
+ * caused by a UI-surface escape hatch up to `needs_human`; callers driving
75
+ * deriveVerdict from `{automation_gate, proof_gate}` get the same answer.
76
+ */
77
+ export declare function deriveVerdict(input: {
78
+ automation_gate: GateStatus;
79
+ proof_gate: GateStatus;
80
+ }): QAVerdict;
45
81
  //# sourceMappingURL=qa-scoring.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"qa-scoring.d.ts","sourceRoot":"","sources":["../../src/types/qa-scoring.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,eAAO,MAAM,aAAa,2EAKhB,CAAA;AACV,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAA;AAExD,mDAAmD;AACnD,MAAM,WAAW,gBAAgB;IAChC,SAAS,EAAE,WAAW,CAAA;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;CACjB;AAED,oDAAoD;AACpD,eAAO,MAAM,uBAAuB;;;CAI1B,CAAA;AAEV,mFAAmF;AACnF,MAAM,WAAW,eAAe;IAC/B,MAAM,EAAE,UAAU,GAAG,qBAAqB,GAAG,UAAU,CAAA;IACvD,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,gBAAgB,EAAE,CAAA;IAC9B,MAAM,EAAE;QACP,KAAK,EAAE;YAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;SAAE,CAAA;QACrE,SAAS,EAAE;YAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,EAAE,CAAA;SAAE,CAAA;QACxD,IAAI,EAAE;YAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAA;SAAE,CAAA;KACnD,CAAA;IACD,MAAM,EAAE,KAAK,CAAC;QACb,QAAQ,EAAE,UAAU,GAAG,SAAS,GAAG,MAAM,CAAA;QACzC,IAAI,EAAE,MAAM,CAAA;QACZ,WAAW,EAAE,MAAM,CAAA;KACnB,CAAC,CAAA;IACF,KAAK,EAAE,MAAM,CAAA;CACb;AAED,2CAA2C;AAC3C,wBAAgB,aAAa,CAAC,MAAM,EAAE,gBAAgB,EAAE,GAAG,eAAe,CAAC,QAAQ,CAAC,CAMnF"}
1
+ {"version":3,"file":"qa-scoring.d.ts","sourceRoot":"","sources":["../../src/types/qa-scoring.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,eAAO,MAAM,aAAa,2EAKhB,CAAA;AACV,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAA;AAExD,mDAAmD;AACnD,MAAM,WAAW,gBAAgB;IAChC,SAAS,EAAE,WAAW,CAAA;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;CACjB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB;;CAE1B,CAAA;AAEV,8EAA8E;AAC9E,MAAM,MAAM,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,aAAa,CAAA;AAE/D,2DAA2D;AAC3D,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,MAAM,CAAA;AAExC,mEAAmE;AACnE,MAAM,WAAW,eAAe;IAC/B,MAAM,EAAE,SAAS,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,gBAAgB,EAAE,CAAA;IAC9B,KAAK,EAAE;QACN,eAAe,EAAE,UAAU,CAAA;QAC3B,UAAU,EAAE,UAAU,CAAA;KACtB,CAAA;IACD,MAAM,EAAE;QACP,KAAK,EAAE;YAAE,MAAM,EAAE,UAAU,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;SAAE,CAAA;QAChE,SAAS,EAAE;YAAE,MAAM,EAAE,UAAU,CAAC;YAAC,MAAM,EAAE,MAAM,EAAE,CAAA;SAAE,CAAA;QACnD,IAAI,EAAE;YAAE,MAAM,EAAE,UAAU,CAAC;YAAC,QAAQ,EAAE,MAAM,CAAA;SAAE,CAAA;KAC9C,CAAA;IACD,MAAM,EAAE,KAAK,CAAC;QACb,QAAQ,EAAE,UAAU,GAAG,SAAS,GAAG,MAAM,CAAA;QACzC,IAAI,EAAE,MAAM,CAAA;QACZ,WAAW,EAAE,MAAM,CAAA;KACnB,CAAC,CAAA;IACF,KAAK,EAAE,MAAM,CAAA;CACb;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE;IACpC,eAAe,EAAE,UAAU,CAAA;IAC3B,UAAU,EAAE,UAAU,CAAA;CACtB,GAAG,SAAS,CAIZ"}
@@ -5,21 +5,42 @@ export const QA_DIMENSIONS = [
5
5
  'code_quality',
6
6
  'test_coverage',
7
7
  ];
8
- /** Threshold constants for QA verdict derivation */
8
+ /**
9
+ * Threshold constants for QA verdict derivation.
10
+ *
11
+ * v0.9.0 (verify-proof-v2-outcome-walker_27-05-26) — `approved_with_notes`
12
+ * is DELETED. Dimensions are now informational rationale only; the verdict
13
+ * derives from the two-gate envelope (`automation_gate` + `proof_gate`),
14
+ * not per-dimension minimums.
15
+ */
9
16
  export const QA_DIMENSION_THRESHOLDS = {
10
- approved: 7, // All dimensions >= 7 = approved
11
- approved_with_notes: 5, // All dimensions >= 5 = approved-with-notes
12
- // Any dimension < 5 = rejected
17
+ approved: 7, // Retained as informational; the verdict derivation no longer reads it.
13
18
  };
14
- /** Derive verdict from dimension scores */
15
- export function deriveVerdict(scores) {
16
- if (scores.length === 0)
19
+ /**
20
+ * Derive the v0.9.0 two-gate verdict from `{automation_gate, proof_gate}`.
21
+ *
22
+ * The legacy per-dimension `deriveVerdict(scores: QADimensionScore[])` is
23
+ * deleted along with `approved_with_notes` — dimensions are now informational
24
+ * rationale on the report.
25
+ *
26
+ * Rules:
27
+ * - `automation_gate: fail` → `rejected` (regardless of `proof_gate`).
28
+ * - `proof_gate: fail` → `needs_human` (signals UI-surface outcomes
29
+ * without proof) when automation is green; this aligns with the
30
+ * workflow-level rule "any test with needs_human_with_transport rolls
31
+ * up to needs_human" (callers that detect a hard fail without a
32
+ * transport hole should pass `proof_gate: 'fail'`).
33
+ * - Both gates pass → `approved`.
34
+ *
35
+ * Per AC13 (spec.md §3): the workflow's Step 7c rolls a `proof_gate: fail`
36
+ * caused by a UI-surface escape hatch up to `needs_human`; callers driving
37
+ * deriveVerdict from `{automation_gate, proof_gate}` get the same answer.
38
+ */
39
+ export function deriveVerdict(input) {
40
+ if (input.automation_gate === 'fail')
17
41
  return 'rejected';
18
- const min = Math.min(...scores.map((s) => s.score));
19
- if (min >= QA_DIMENSION_THRESHOLDS.approved)
20
- return 'approved';
21
- if (min >= QA_DIMENSION_THRESHOLDS.approved_with_notes)
22
- return 'approved-with-notes';
23
- return 'rejected';
42
+ if (input.proof_gate === 'fail')
43
+ return 'needs_human';
44
+ return 'approved';
24
45
  }
25
46
  //# sourceMappingURL=qa-scoring.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"qa-scoring.js","sourceRoot":"","sources":["../../src/types/qa-scoring.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,MAAM,CAAC,MAAM,aAAa,GAAG;IAC5B,cAAc;IACd,aAAa;IACb,cAAc;IACd,eAAe;CACN,CAAA;AAUV,oDAAoD;AACpD,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACtC,QAAQ,EAAE,CAAC,EAAE,iCAAiC;IAC9C,mBAAmB,EAAE,CAAC,EAAE,4CAA4C;IACpE,+BAA+B;CACtB,CAAA;AAqBV,2CAA2C;AAC3C,MAAM,UAAU,aAAa,CAAC,MAA0B;IACvD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,UAAU,CAAA;IAC1C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAA;IACnD,IAAI,GAAG,IAAI,uBAAuB,CAAC,QAAQ;QAAE,OAAO,UAAU,CAAA;IAC9D,IAAI,GAAG,IAAI,uBAAuB,CAAC,mBAAmB;QAAE,OAAO,qBAAqB,CAAA;IACpF,OAAO,UAAU,CAAA;AAClB,CAAC"}
1
+ {"version":3,"file":"qa-scoring.js","sourceRoot":"","sources":["../../src/types/qa-scoring.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,MAAM,CAAC,MAAM,aAAa,GAAG;IAC5B,cAAc;IACd,aAAa;IACb,cAAc;IACd,eAAe;CACN,CAAA;AAUV;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACtC,QAAQ,EAAE,CAAC,EAAE,wEAAwE;CAC5E,CAAA;AA+BV;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,aAAa,CAAC,KAG7B;IACA,IAAI,KAAK,CAAC,eAAe,KAAK,MAAM;QAAE,OAAO,UAAU,CAAA;IACvD,IAAI,KAAK,CAAC,UAAU,KAAK,MAAM;QAAE,OAAO,aAAa,CAAA;IACrD,OAAO,UAAU,CAAA;AAClB,CAAC"}
@@ -24,12 +24,17 @@ export interface ActiveTask {
24
24
  phase: TaskPhase;
25
25
  progress: TaskProgress;
26
26
  }
27
- /** A task that has been completed */
27
+ /**
28
+ * A task that has been completed.
29
+ *
30
+ * v0.9.0 — `qa_verdict` is the closed set `{approved, rejected, needs_human}`.
31
+ * The legacy `approved_with_notes` middle band is DELETED (no shim).
32
+ */
28
33
  export interface CompletedTask {
29
34
  id: string;
30
35
  description: string;
31
36
  completed_at: string;
32
- qa_verdict: 'approved' | 'approved_with_notes' | 'rejected';
37
+ qa_verdict: 'approved' | 'rejected' | 'needs_human';
33
38
  commits: string[];
34
39
  }
35
40
  /** A decision recorded during task execution */
@@ -1 +1 @@
1
- {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/types/state.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,wDAAwD;AACxD,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,UAAU,GAAG,MAAM,GAAG,OAAO,CAAA;AAE/D,sCAAsC;AACtC,MAAM,MAAM,SAAS,GAClB,YAAY,GACZ,UAAU,GACV,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,QAAQ,CAAA;AAEX,6CAA6C;AAC7C,MAAM,WAAW,YAAY;IAC5B,cAAc,EAAE,MAAM,CAAA;IACtB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B;AAED,oDAAoD;AACpD,MAAM,WAAW,UAAU;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,EAAE,SAAS,CAAA;IAChB,QAAQ,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IACvB,KAAK,EAAE,SAAS,CAAA;IAChB,QAAQ,EAAE,YAAY,CAAA;CACtB;AAED,qCAAqC;AACrC,MAAM,WAAW,aAAa;IAC7B,EAAE,EAAE,MAAM,CAAA;IACV,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,UAAU,GAAG,qBAAqB,GAAG,UAAU,CAAA;IAC3D,OAAO,EAAE,MAAM,EAAE,CAAA;CACjB;AAED,gDAAgD;AAChD,MAAM,WAAW,YAAY;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;CACZ;AAED,2DAA2D;AAC3D,MAAM,WAAW,YAAY;IAC5B,OAAO,EAAE,MAAM,CAAA;IACf,cAAc,EAAE,MAAM,CAAA;IACtB,aAAa,EAAE,MAAM,CAAA;IACrB,WAAW,EAAE,UAAU,GAAG,IAAI,CAAA;IAC9B,eAAe,EAAE,aAAa,EAAE,CAAA;IAChC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;IACvC,+DAA+D;IAC/D,SAAS,EAAE,OAAO,CAAA;IAClB,oEAAoE;IACpE,UAAU,CAAC,EAAE;QACZ,MAAM,EAAE,OAAO,CAAA;QACf,wBAAwB,EAAE,MAAM,CAAA;QAChC,YAAY,EAAE,MAAM,CAAA;KACpB,CAAA;CACD"}
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/types/state.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,wDAAwD;AACxD,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,UAAU,GAAG,MAAM,GAAG,OAAO,CAAA;AAE/D,sCAAsC;AACtC,MAAM,MAAM,SAAS,GAClB,YAAY,GACZ,UAAU,GACV,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,QAAQ,CAAA;AAEX,6CAA6C;AAC7C,MAAM,WAAW,YAAY;IAC5B,cAAc,EAAE,MAAM,CAAA;IACtB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B;AAED,oDAAoD;AACpD,MAAM,WAAW,UAAU;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,EAAE,SAAS,CAAA;IAChB,QAAQ,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IACvB,KAAK,EAAE,SAAS,CAAA;IAChB,QAAQ,EAAE,YAAY,CAAA;CACtB;AAED;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC7B,EAAE,EAAE,MAAM,CAAA;IACV,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,UAAU,GAAG,UAAU,GAAG,aAAa,CAAA;IACnD,OAAO,EAAE,MAAM,EAAE,CAAA;CACjB;AAED,gDAAgD;AAChD,MAAM,WAAW,YAAY;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;CACZ;AAED,2DAA2D;AAC3D,MAAM,WAAW,YAAY;IAC5B,OAAO,EAAE,MAAM,CAAA;IACf,cAAc,EAAE,MAAM,CAAA;IACtB,aAAa,EAAE,MAAM,CAAA;IACrB,WAAW,EAAE,UAAU,GAAG,IAAI,CAAA;IAC9B,eAAe,EAAE,aAAa,EAAE,CAAA;IAChC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;IACvC,+DAA+D;IAC/D,SAAS,EAAE,OAAO,CAAA;IAClB,oEAAoE;IACpE,UAAU,CAAC,EAAE;QACZ,MAAM,EAAE,OAAO,CAAA;QACf,wBAAwB,EAAE,MAAM,CAAA;QAChC,YAAY,EAAE,MAAM,CAAA;KACpB,CAAA;CACD"}
@@ -62,13 +62,21 @@ export interface TaskRecord {
62
62
  */
63
63
  pr_url?: string;
64
64
  }
65
- /** Completed task row stored under `state.completed_tasks[]`. */
65
+ /**
66
+ * Completed task row stored under `state.completed_tasks[]`.
67
+ *
68
+ * v0.9.0 — `qa_verdict` is the closed set
69
+ * `{approved, rejected, needs_human, completed}`. The legacy
70
+ * `approved_with_notes` middle band is DELETED (no shim). `completed`
71
+ * remains as a phase-only marker for tasks that closed without a
72
+ * verify-proof verdict.
73
+ */
66
74
  export interface CompletedTaskRecord {
67
75
  id: string;
68
76
  description: string;
69
77
  scope: Scope;
70
78
  completed_at: string;
71
- qa_verdict: 'approved' | 'approved_with_notes' | 'rejected' | 'completed';
79
+ qa_verdict: 'approved' | 'rejected' | 'needs_human' | 'completed';
72
80
  commits: string[];
73
81
  lifecycle_phase?: LifecyclePhase;
74
82
  }
@@ -1 +1 @@
1
- {"version":3,"file":"task-record.d.ts","sourceRoot":"","sources":["../../src/types/task-record.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AACjD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAEpC,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,UAAU,GAAG,MAAM,GAAG,OAAO,CAAA;AAE/D;iEACiE;AACjE,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE;QACL,KAAK,EAAE,KAAK,CAAA;QACZ,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;QAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;KACvB,CAAA;IACD,EAAE,EAAE;QACH,KAAK,EAAE,KAAK,CAAA;QACZ,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;QAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;KACvB,CAAA;IACD,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;qEACiE;IACjE,cAAc,CAAC,EAAE,OAAO,CAAA;CACxB;AAED,MAAM,WAAW,YAAY;IAC5B,cAAc,EAAE,MAAM,CAAA;IACtB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B;AAED,MAAM,WAAW,UAAU;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,EAAE,SAAS,CAAA;IAChB,QAAQ,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IACvB,KAAK,EAAE,KAAK,CAAA;IACZ,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,eAAe,EAAE,cAAc,CAAA;IAC/B,OAAO,EAAE,gBAAgB,EAAE,CAAA;IAC3B,QAAQ,EAAE,YAAY,CAAA;IACtB,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;CACf;AAED,iEAAiE;AACjE,MAAM,WAAW,mBAAmB;IACnC,EAAE,EAAE,MAAM,CAAA;IACV,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,EAAE,KAAK,CAAA;IACZ,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,UAAU,GAAG,qBAAqB,GAAG,UAAU,GAAG,WAAW,CAAA;IACzE,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,eAAe,CAAC,EAAE,cAAc,CAAA;CAChC"}
1
+ {"version":3,"file":"task-record.d.ts","sourceRoot":"","sources":["../../src/types/task-record.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AACjD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAEpC,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,UAAU,GAAG,MAAM,GAAG,OAAO,CAAA;AAE/D;iEACiE;AACjE,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE;QACL,KAAK,EAAE,KAAK,CAAA;QACZ,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;QAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;KACvB,CAAA;IACD,EAAE,EAAE;QACH,KAAK,EAAE,KAAK,CAAA;QACZ,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;QAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;KACvB,CAAA;IACD,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;qEACiE;IACjE,cAAc,CAAC,EAAE,OAAO,CAAA;CACxB;AAED,MAAM,WAAW,YAAY;IAC5B,cAAc,EAAE,MAAM,CAAA;IACtB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B;AAED,MAAM,WAAW,UAAU;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,EAAE,SAAS,CAAA;IAChB,QAAQ,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IACvB,KAAK,EAAE,KAAK,CAAA;IACZ,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,eAAe,EAAE,cAAc,CAAA;IAC/B,OAAO,EAAE,gBAAgB,EAAE,CAAA;IAC3B,QAAQ,EAAE,YAAY,CAAA;IACtB,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;CACf;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,mBAAmB;IACnC,EAAE,EAAE,MAAM,CAAA;IACV,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,EAAE,KAAK,CAAA;IACZ,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,UAAU,GAAG,UAAU,GAAG,aAAa,GAAG,WAAW,CAAA;IACjE,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,eAAe,CAAC,EAAE,cAAc,CAAA;CAChC"}
@@ -0,0 +1,40 @@
1
+ # mobile driver
2
+
3
+ Unified iOS + Android UI-driving verify-proof driver. Wraps the
4
+ [`mobile-mcp`](https://github.com/mobile-next/mobile-mcp) MCP server
5
+ (mobile-next, Apache-2.0) — one backend for both platforms, driven via the
6
+ native accessibility tree with screenshot-coordinate fallback (ADB for
7
+ Android, idb for iOS).
8
+
9
+ - **Targets:** `ios`, `android`, `expo`, `react-native`.
10
+ - **Transport:** `{ mcp_server_id: "mobile-mcp" }` (provisioned as
11
+ `npx -y @mobilenext/mobile-mcp@latest`).
12
+ - **Platform selection:** chosen INSIDE the tool call
13
+ (`mobile_list_available_devices` → device id), NOT at resolver time — that
14
+ is why one driver covers both platforms (decision D-01).
15
+
16
+ ## Lifecycle boundary
17
+
18
+ This driver only **drives UI + captures evidence** against a foregrounded,
19
+ JS-ready app. The app lifecycle — build, install, launch, Metro start, and
20
+ the launch-readiness gate — is owned by `apt-tools mobile prepare`
21
+ (decisions D-03/D-04), which runs BEFORE any verb is dispatched.
22
+
23
+ ## System prerequisites (validated, not installed)
24
+
25
+ `mobile-mcp` is pure npm, but the underlying device toolchain is NOT:
26
+
27
+ - **iOS:** `idb-companion` (`brew install idb-companion`) + Xcode + a booted
28
+ Simulator.
29
+ - **Android:** Android SDK + `adb` (`$ANDROID_HOME/platform-tools`) + an
30
+ emulator/device.
31
+
32
+ These are validated by the manifest's `preflight[]` via
33
+ `apt-tools driver doctor` (which records `missing_deps` with guidance) — the
34
+ framework never auto-installs them (decision D-09).
35
+
36
+ ## Out of scope
37
+
38
+ Real-device runs and Mac Catalyst (separate RFCs). XcodeBuildMCP is an
39
+ optional future escalation for native-Xcode-heavy iOS builds, not a
40
+ dependency of this driver (decision D-02).
@@ -0,0 +1,106 @@
1
+ /**
2
+ * mobile/driver.mjs — bundled unified mobile driver (iOS + Android).
3
+ *
4
+ * Wraps the `mobile-mcp` MCP server (mobile-next/mobile-mcp, Apache-2.0),
5
+ * the single UI-driving backend for BOTH platforms (decision D-01). The
6
+ * platform/device is chosen INSIDE the tool call (mobile_list_available_
7
+ * devices → device id), NOT at resolver time — that is why one driver
8
+ * targets ios/android/expo/react-native.
9
+ *
10
+ * The app lifecycle (build / install / launch / Metro / ready-gate) is
11
+ * owned by `apt-tools mobile prepare`, NOT this driver (D-03/D-04). This
12
+ * driver assumes a foregrounded, JS-ready app and only drives UI + captures
13
+ * evidence.
14
+ *
15
+ * Per ID-01 — the workflow prose NEVER names mcp__mobile-mcp__* tools; the
16
+ * verb→tool mapping lives ONLY here. Transport dispatch happens at the
17
+ * workflow-runner layer (which holds the model's tool surface).
18
+ *
19
+ * IDL verb → mobile-mcp tool mapping:
20
+ * launch → mobile_use_device (select the booted device/simulator)
21
+ * click → mobile_click_on_screen_at_coordinates
22
+ * type → mobile_type_keys
23
+ * key → mobile_press_button
24
+ * swipe → mobile_swipe_on_screen
25
+ * screenshot → mobile_save_screenshot
26
+ * capture_logs → mobile_device_logs
27
+ * assert_visible → mobile_list_elements_on_screen (element present)
28
+ * assert_text → mobile_list_elements_on_screen (text match)
29
+ * teardown → mobile_terminate_app (best-effort)
30
+ *
31
+ * The driver is a thin adapter: it validates input shape via the IDL
32
+ * arg-metadata and returns a VerbResult. The actual transport dispatch
33
+ * happens at the workflow runner layer. For conformance testing we expose
34
+ * the verb signatures as identity stubs returning VerbResult — the SDK
35
+ * conformance kit then validates the shape contract.
36
+ */
37
+
38
+ import { validateVerbArgs } from '../../src/cli/verify-proof/idl/index.mjs'
39
+
40
+ /** Build a success VerbResult. */
41
+ function ok(evidence_paths = []) {
42
+ return { status: 'success', evidence_paths, duration_ms: 0, retry_count: 0 }
43
+ }
44
+ function fail(error) {
45
+ return { status: 'fail', evidence_paths: [], duration_ms: 0, retry_count: 0, error }
46
+ }
47
+
48
+ /**
49
+ * Validate args + return a VerbResult shape stub. Real transport invocation
50
+ * happens at the workflow-runner layer (model holds the tool surface —
51
+ * mcp__mobile-mcp__*).
52
+ *
53
+ * @param {string} verb
54
+ * @param {Record<string, unknown>} args
55
+ * @returns {{status: 'success'|'fail'|'skip', evidence_paths: string[], duration_ms: number, retry_count: number, error?: string}}
56
+ */
57
+ function adaptVerb(verb, args) {
58
+ const check = validateVerbArgs(verb, args)
59
+ if (!check.valid) {
60
+ return fail(`mobile.${verb}: missing required args: ${check.missing.join(', ')}`)
61
+ }
62
+ if (verb === 'screenshot' && typeof args.output_path === 'string') {
63
+ return ok([args.output_path])
64
+ }
65
+ return ok([])
66
+ }
67
+
68
+ export async function launch(args) {
69
+ return adaptVerb('launch', args)
70
+ }
71
+
72
+ export async function click(args) {
73
+ return adaptVerb('click', args)
74
+ }
75
+
76
+ export async function type(args) {
77
+ return adaptVerb('type', args)
78
+ }
79
+
80
+ export async function key(args) {
81
+ return adaptVerb('key', args)
82
+ }
83
+
84
+ export async function swipe(args) {
85
+ return adaptVerb('swipe', args)
86
+ }
87
+
88
+ export async function screenshot(args) {
89
+ return adaptVerb('screenshot', args)
90
+ }
91
+
92
+ export async function capture_logs(args) {
93
+ return adaptVerb('capture_logs', args)
94
+ }
95
+
96
+ export async function assert_visible(args) {
97
+ return adaptVerb('assert_visible', args)
98
+ }
99
+
100
+ export async function assert_text(args) {
101
+ return adaptVerb('assert_text', args)
102
+ }
103
+
104
+ export async function teardown(args) {
105
+ return adaptVerb('teardown', args)
106
+ }
@@ -0,0 +1,49 @@
1
+ {
2
+ "driverId": "mobile",
3
+ "version": "0.1.0",
4
+ "targets": ["ios", "android", "expo", "react-native"],
5
+ "capabilities": [
6
+ "launch",
7
+ "click",
8
+ "type",
9
+ "key",
10
+ "swipe",
11
+ "screenshot",
12
+ "capture_logs",
13
+ "assert_visible",
14
+ "assert_text",
15
+ "teardown"
16
+ ],
17
+ "evidence_capabilities": ["screenshot", "capture_logs"],
18
+ "frameworkApiRange": "^1.0.0",
19
+ "priority": 80,
20
+ "stability": "ga",
21
+ "trust": {
22
+ "tier": "first_party_signed",
23
+ "publisher": {
24
+ "name": "Aperant",
25
+ "npm_scope": "@aperant",
26
+ "github_org": "Mikalsen-AI",
27
+ "verified": true
28
+ }
29
+ },
30
+ "security": {
31
+ "sandbox_profile": "workspace_write",
32
+ "network": "deny",
33
+ "requires_user_approval": false
34
+ },
35
+ "audit": { "log_invocations": true, "log_manifest_hash": true },
36
+ "preflight": [
37
+ {
38
+ "name": "xcrun (iOS Simulator toolchain)",
39
+ "command": { "binary": "xcrun", "args": ["simctl", "help"] },
40
+ "required": false
41
+ },
42
+ {
43
+ "name": "adb (Android platform-tools)",
44
+ "command": { "binary": "adb", "args": ["--version"] },
45
+ "required": false
46
+ }
47
+ ],
48
+ "transport": { "mcp_server_id": "mobile-mcp" }
49
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aperant/framework",
3
- "version": "0.10.0",
3
+ "version": "0.12.0",
4
4
  "description": "AI coding framework — composable skills for planning, executing, verifying, and reviewing code with any LLM provider. Works as Claude Code commands, Codex tasks, or programmatically.",
5
5
  "author": "Mikalsen AI <hello@mikalsen.ai>",
6
6
  "type": "module",
@@ -0,0 +1,11 @@
1
+ Run one read-only clarification turn using Work Record context, prior chat history, and the latest user response.
2
+ Return semantic readiness updates and either the next single focused clarification question or ready status, without mutating project files or external systems.
3
+
4
+ You are the Aperant clarification agent.
5
+ First call readPhaseOutput with phase="triage" to inspect the triage output.
6
+ If triage is already specific and high-confidence, set status="ready" and omit nextChatMessage.
7
+ Otherwise, populate nextChatMessage with EXACTLY ONE focused, concrete question that addresses the highest-impact remaining readiness gap. Do not batch multiple questions into one turn.
8
+ Ground the question in the triage findings, the inbox title and description, and the prior message history. Acknowledge what the user already said when relevant.
9
+ Set nextChatMessage.dimensionKey to the readiness dimension the question targets, and nextChatMessage.cardId to the matching open clarification card when one exists.
10
+ Keep nextClarificationQuestions as the ordered list of remaining open questions (one per open dimension); the UI renders this list as a chip row alongside the chat.
11
+ Use repository tools only when needed to sharpen the question.
@@ -0,0 +1,20 @@
1
+ Refine one Inbox Work Item into structured triage output.
2
+ Use supplied repository evidence and preserve read-only behavior.
3
+ Return semantic findings, readiness evidence, and clarification prompts without mutating project files or external systems.
4
+
5
+ You are the Aperant triage agent.
6
+ Produce structured JSON only via the provided schema.
7
+ Semantic capability mapping: repository_evidence maps to listFiles and readFile tools.
8
+ Use listFiles and readFile tools to ground claims in repository evidence.
9
+ Return a triage dossier with repo-evidenced facts, inferred assumptions, risks, acceptance criteria, and clarification plan.
10
+ Always include readinessEvaluation with rubricVersion, dimensions, missingDimensions, hardBlockers, and justificationSummary.
11
+ Universal readiness keys: problem_clarity, scope_definition, repo_evidence, risk_identification, acceptance_criteria_quality.
12
+ Bug-specific keys: repro_steps, expected_vs_actual, impact_assessment.
13
+ Feature-specific keys: user_value, solution_shape, dependency_mapping.
14
+ Enhancement-specific keys: current_behavior_baseline, incremental_scope, migration_safety.
15
+ Every readiness dimension requires key, score (0-100), evidence, justification, and optional hardBlocker.
16
+ Only place verified repository findings in repoEvidencedFacts.
17
+ For every inferred assumption, set disposition to requires_hitl_confirmation or accepted_assumption.
18
+ Do not present product commitments as facts unless repository evidence directly supports them.
19
+ Each proposed acceptance criterion must include a verificationMethod and Given/When/Then fields when behavior is being specified.
20
+ Prefer concise summaries and concrete touch points.
@@ -66,10 +66,10 @@ The reviewer writes `qa_report.md` and updates `implementation_plan.json` with a
66
66
 
67
67
  Read the `qa_signoff` from `implementation_plan.json`. The reviewer now writes dimension scores alongside the status.
68
68
 
69
- Read the dimension scores:
70
- - If ALL dimensions >= 7 and status is "approved" -> Build passes. Write final QA report. Done.
71
- - If ALL dimensions >= 5 and status is "approved-with-notes" -> Accept with notes. Write final QA report with improvement suggestions. Done.
72
- - If ANY dimension < 5 -> Analyze which dimensions are lowest (Phase 4)
69
+ Read the gates + dimension scores:
70
+ - If `automation_gate: pass` AND `proof_gate: pass` AND status is "approved" -> Build passes. Write final QA report. Done.
71
+ - If `automation_gate: pass` AND `proof_gate: fail` AND status is "needs_human" -> Transport gap. Write final QA report listing what the human must install (e.g. an MCP server). Done.
72
+ - If `automation_gate: fail` OR critical findings unresolved -> status is "rejected". Spawn fixer (Phase 4).
73
73
  - If no scores written -> Reviewer failed to score. Re-spawn with explicit scoring instructions: "You MUST score all 4 dimensions (completeness, correctness, code_quality, test_coverage) on 1-10 before writing a verdict."
74
74
  - If no signoff written at all -> Reviewer failed to update the file. Retry with explicit instructions.
75
75
 
@@ -100,10 +100,10 @@ When the reviewer rejects, triage based on dimension scores from the qa_signoff,
100
100
  - Documentation formatting
101
101
  - Non-functional improvements
102
102
 
103
- **Decision Framework:**
104
- - If ONLY cosmetic issues and ALL dimension scores >= 5 -> approve with notes (write qa_signoff: approved-with-notes)
105
- - If ALL dimension scores >= 7 and no critical issues -> approve the build (write qa_signoff: approved)
106
- - If ANY dimension < 5 -> spawn qa_fixer with targeted guidance focused on the lowest-scoring dimension
103
+ **Decision Framework (v0.9.0 two-gate envelope):**
104
+ - If `automation_gate: pass` AND `proof_gate: pass` AND no critical issues -> approve the build (write qa_signoff: approved)
105
+ - If `automation_gate: pass` AND `proof_gate: fail` (UI-surface outcome missing driver transport) -> write qa_signoff: needs_human with a `transport_gap` array
106
+ - If `automation_gate: fail` OR critical issues unresolved -> spawn qa_fixer with targeted guidance
107
107
  - If the same critical issue appears 3+ times -> escalate to human
108
108
 
109
109
  ### Phase 5: Fix Cycle
@@ -137,19 +137,20 @@ Track iteration count. Your goal is to converge quickly:
137
137
 
138
138
  ## QUALITY GATES
139
139
 
140
- ### Approval Criteria (Score-Based)
140
+ ### Approval Criteria (v0.9.0 two-gate envelope)
141
141
  Approve when ALL of these are true:
142
- - All 4 dimension scores >= 7 (for "approved") or >= 5 (for "approved-with-notes")
142
+ - `automation_gate: pass` (tests + typecheck + lint all green)
143
+ - `proof_gate: pass` (every outcome has surface-appropriate evidence)
143
144
  - No critical issues in the issues array
144
- - Automated checks (tests, typecheck, lint) all pass
145
+ - Dimensions land >= 7 as informational rationale (does NOT drive the verdict)
145
146
 
146
- ### Score Thresholds
147
- - **approved**: ALL dimensions >= 7 AND no critical issues
148
- - **approved-with-notes**: ALL dimensions >= 5 AND no critical issues
149
- - **rejected**: ANY dimension < 5 OR critical issues found
147
+ ### Verdict Closed Set
148
+ - **approved**: `automation_gate: pass` AND `proof_gate: pass` AND no critical issues
149
+ - **needs_human**: `automation_gate: pass` AND `proof_gate: fail` (transport gap)
150
+ - **rejected**: `automation_gate: fail` OR critical issues unresolved
150
151
 
151
- ### Acceptable Imperfections (for approved-with-notes)
152
- These should NOT block approval when all dimensions are >= 5:
152
+ ### Acceptable Imperfections (cited in `findings.notes` on an `approved` verdict)
153
+ These do NOT block approval and ride along as notes:
153
154
  - Missing optional features (if spec marks them as optional)
154
155
  - Code style deviations (if functionality is correct)
155
156
  - Missing edge case handling for unlikely scenarios
@@ -37,13 +37,15 @@ You MUST score 4 dimensions on a 1-10 scale. The verdict is DERIVED from scores
37
37
  **3-4 (Poor):** Significant problems. Multiple issues.
38
38
  **1-2 (Failed):** Fundamentally broken or missing.
39
39
 
40
- ### Verdict Derivation (NOT your choice -- derived from scores)
40
+ ### Verdict Derivation (v0.9.0 two-gate envelope; NOT your choice -- derived from gates)
41
41
 
42
- - **approved**: ALL dimensions >= 7 AND no critical issues
43
- - **approved-with-notes**: ALL dimensions >= 5 AND no critical issues
44
- - **rejected**: ANY dimension < 5 OR critical issues found
42
+ Verdict is the closed set `{approved, rejected, needs_human}`. It derives from the two-gate envelope (`automation_gate` AND `proof_gate`) per `/apt:verify-proof`. Dimensions are informational rationale only.
45
43
 
46
- You MUST write scores BEFORE writing the verdict. The verdict follows from the scores.
44
+ - **approved**: `automation_gate: pass` AND `proof_gate: pass` AND no critical findings.
45
+ - **needs_human**: `automation_gate: pass` AND `proof_gate: fail` (typically: UI-surface outcome with no driver transport — see verify-proof workflow).
46
+ - **rejected**: `automation_gate: fail` OR critical findings unresolved.
47
+
48
+ You MUST write scores BEFORE writing the verdict. The verdict follows from the gates, with dimensions as rationale.
47
49
 
48
50
  ---
49
51
 
@@ -440,7 +442,7 @@ Create a comprehensive QA report with dimension scores:
440
442
  | test_coverage | {N}/10 | {evidence} |
441
443
 
442
444
  **Minimum score:** {min}
443
- **Derived verdict:** {approved|approved-with-notes|rejected}
445
+ **Derived verdict:** {approved|rejected|needs_human}
444
446
 
445
447
  ## Summary
446
448
 
@@ -514,7 +516,7 @@ For each critical/major issue, describe what the Coder Agent should do:
514
516
 
515
517
  **Next Steps**:
516
518
  - [If approved: Ready for merge]
517
- - [If approved-with-notes: List notes for future improvement]
519
+ - [If needs_human: Install the missing driver transport (e.g. MCP server) and re-run /apt:verify-proof]
518
520
  - [If rejected: List of fixes needed, then re-run QA]
519
521
  ```
520
522
 
@@ -566,17 +568,17 @@ EOF
566
568
  # Only commit actual code changes to the project
567
569
  ```
568
570
 
569
- ### If APPROVED-WITH-NOTES:
571
+ ### If NEEDS_HUMAN (transport gap):
570
572
 
571
- Same as approved, but with status "approved-with-notes" and an additional `notes` array:
573
+ Same as approved, but with status `"needs_human"` and a `transport_gap` array describing what the human must install (e.g. an MCP server, a binary):
572
574
 
573
575
  ```json
574
576
  {
575
577
  "qa_signoff": {
576
- "status": "approved-with-notes",
577
- "notes": [
578
- "Minor issue: [description]",
579
- "Suggestion: [description]"
578
+ "status": "needs_human",
579
+ "transport_gap": [
580
+ "Missing MCP server: puppeteer (drivers/browser/manifest.json :: transport.mcp_server_id)",
581
+ "After install, re-run /apt:verify-proof"
580
582
  ],
581
583
  "dimension_scores": {
582
584
  "completeness": "[N]",
@@ -762,11 +764,11 @@ QA will automatically re-run after fixes.
762
764
 
763
765
  The QA -> Fix -> QA loop continues until:
764
766
 
765
- 1. **All dimension scores >= 7** (for approved) or **>= 5** (for approved-with-notes)
767
+ 1. **`automation_gate: pass`** (lint + typecheck + tests all green) AND **`proof_gate: pass`** (every outcome has surface-appropriate evidence)
766
768
  2. **No critical issues remain**
767
769
  3. **All tests pass**
768
770
  4. **No regressions**
769
- 5. **QA derives approved or approved-with-notes verdict**
771
+ 5. **QA derives `approved`** (the only ship-eligible verdict; `needs_human` requires installing the missing transport, `rejected` requires code fixes)
770
772
 
771
773
  Maximum iterations: 5 (configurable)
772
774
 
@@ -75,6 +75,34 @@ SessionStart hook — zero latency on /apt. If `update_check` is null,
75
75
  no chip. Skip the chip entirely once the user has run `/apt:update`
76
76
  in the current session (the cache is cleared there).
77
77
 
78
+ **Catch-up panel (runtime-agnostic):** if the route envelope includes a
79
+ non-null `catch_up` field, print the combined panel BEFORE the mode handler
80
+ runs, regardless of runtime (Claude Code, Codex, Gemini, Cursor, …). The
81
+ panel has two halves; render `catch_up.welcome_back` first, then
82
+ `catch_up.team_digest`. Omit a half whose field is null.
83
+
84
+ If `catch_up.welcome_back` is present (your own half-finished work):
85
+
86
+ ```
87
+ Where you left off: {description} — {lifecycle_phase}{current_subtask ? " · subtask " + current_subtask : ""} ({relative_time})
88
+ {next_step ? " Next: " + next_step : ""}
89
+ {others_count > 0 ? " (+" + others_count + " others · /apt:resume)" : ""}
90
+ ```
91
+
92
+ If `catch_up.team_digest` is present (what teammates merged while you were away):
93
+
94
+ ```
95
+ While you were away:
96
+ {for each entry in team_digest.entries: " #" + number + " " + title + " — " + author}
97
+ {team_digest.more_count > 0 ? " +" + team_digest.more_count + " more on main" : ""}
98
+ ```
99
+
100
+ This is display-only here — the Last-Seen Marker advance happens in route.mjs,
101
+ not in this skill. The panel is computed lazily on interactive (TTY) calls
102
+ only; non-TTY/background/spawned calls carry no `catch_up` field. Do not call
103
+ it an "activity feed", "changelog", or "notifications" — it is the
104
+ Welcome-Back Summary + Team-Change Digest.
105
+
78
106
  Then proceed to the matching mode handler below. Do NOT deviate from the routing decision.
79
107
  </mandatory_first_step>
80
108
 
@@ -129,6 +157,7 @@ Show available commands table:
129
157
  | `apt:scan` | Discover standards + refresh feature registry |
130
158
  | `apt:docs` | Generate/update documentation |
131
159
  | `apt:resume` | Restore context after session break |
160
+ | `apt:catch-up` | Re-view the catch-up panel (Welcome-Back + Team-Change Digest) — never advances the Last-Seen Marker |
132
161
  | `apt:pause` | Human-initiated handoff — save state when YOU need to stop |
133
162
  | `apt:ship` | Create PR with traceability |
134
163
  | `apt:close-task` | Post-merge closer — confirm PR merged, flip phase, drain narration ledger |