@geminix/gxpm 0.1.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 (299) hide show
  1. package/AGENTS.md +148 -0
  2. package/CANON.md +53 -0
  3. package/CLAUDE.md +60 -0
  4. package/CONTEXT.md +49 -0
  5. package/DEBUG.md +59 -0
  6. package/ISSUE_CONTEXT.md +25 -0
  7. package/README.md +143 -0
  8. package/VERSION +1 -0
  9. package/agents/cleanup-auditor/cleanup-auditor.md +56 -0
  10. package/agents/grill-master.md +26 -0
  11. package/agents/implementer.md +32 -0
  12. package/agents/review-army/accessibility-reviewer.md +54 -0
  13. package/agents/review-army/code-quality-reviewer.md +54 -0
  14. package/agents/review-army/security-reviewer.md +56 -0
  15. package/agents/review-army/spec-compliance-reviewer.md +51 -0
  16. package/agents/review-army/test-reviewer.md +55 -0
  17. package/agents/reviewer.md +59 -0
  18. package/agents/ship-audit-army/docs-auditor.md +53 -0
  19. package/agents/ship-audit-army/performance-auditor.md +52 -0
  20. package/agents/ship-audit-army/security-auditor.md +52 -0
  21. package/agents/specifier.md +55 -0
  22. package/agents/triage-officer.md +27 -0
  23. package/bin/gxpm +17 -0
  24. package/bin/gxpm-browser +17 -0
  25. package/bin/gxpm-config +15 -0
  26. package/bin/gxpm-eval +13 -0
  27. package/bin/gxpm-global-discover +15 -0
  28. package/bin/gxpm-init +38 -0
  29. package/bin/gxpm-investigate +194 -0
  30. package/bin/gxpm-uninstall +15 -0
  31. package/bin/gxpm-update-check +165 -0
  32. package/commands/build.md +40 -0
  33. package/commands/help.md +53 -0
  34. package/commands/plan.md +34 -0
  35. package/commands/refine.md +46 -0
  36. package/commands/review.md +34 -0
  37. package/commands/ship.md +37 -0
  38. package/core/ac-check.ts +20 -0
  39. package/core/agent-runtime.ts +363 -0
  40. package/core/artifact-validator.ts +151 -0
  41. package/core/artifacts.ts +313 -0
  42. package/core/autopilot.ts +250 -0
  43. package/core/capabilities.ts +779 -0
  44. package/core/checkpoint.ts +370 -0
  45. package/core/cleanup.ts +32 -0
  46. package/core/command-probe.ts +82 -0
  47. package/core/config.ts +533 -0
  48. package/core/contracts/behavior-spec.schema.ts +38 -0
  49. package/core/contracts/converter.ts +61 -0
  50. package/core/contracts/host.ts +43 -0
  51. package/core/converters/converter.ts +93 -0
  52. package/core/converters/index.ts +8 -0
  53. package/core/converters/managed-artifact.ts +119 -0
  54. package/core/converters/parser.ts +159 -0
  55. package/core/converters/template-renderer.ts +35 -0
  56. package/core/converters/writer.ts +61 -0
  57. package/core/dag-executor.ts +426 -0
  58. package/core/dag-loader.ts +292 -0
  59. package/core/dag-schemas.ts +150 -0
  60. package/core/dispatch.ts +125 -0
  61. package/core/evidence.ts +148 -0
  62. package/core/gate.ts +269 -0
  63. package/core/hook-engine.ts +566 -0
  64. package/core/host-probe.ts +64 -0
  65. package/core/implement.ts +16 -0
  66. package/core/isolation-errors.ts +174 -0
  67. package/core/isolation-resolver.ts +921 -0
  68. package/core/issue-context.ts +381 -0
  69. package/core/issue-readiness.ts +457 -0
  70. package/core/issue-sync.ts +427 -0
  71. package/core/issues.ts +132 -0
  72. package/core/land.ts +108 -0
  73. package/core/orchestrator.ts +54 -0
  74. package/core/phase-artifact.ts +32 -0
  75. package/core/phase-gates.ts +130 -0
  76. package/core/phase-rewind.ts +94 -0
  77. package/core/plan-lint.ts +61 -0
  78. package/core/plan.ts +77 -0
  79. package/core/port-allocation.ts +50 -0
  80. package/core/pr-check.ts +15 -0
  81. package/core/preset-system/preset-resolver.ts +221 -0
  82. package/core/project-init-status.ts +127 -0
  83. package/core/qa.ts +15 -0
  84. package/core/resilience.ts +165 -0
  85. package/core/runs.ts +288 -0
  86. package/core/safe-path.test.ts +80 -0
  87. package/core/safe-path.ts +60 -0
  88. package/core/sdd-gate.test.ts +98 -0
  89. package/core/sdd-gate.ts +134 -0
  90. package/core/self-review.ts +62 -0
  91. package/core/session.ts +70 -0
  92. package/core/ship.ts +86 -0
  93. package/core/specify.ts +173 -0
  94. package/core/state.ts +1002 -0
  95. package/core/template-engine.ts +152 -0
  96. package/core/template-resolver.test.ts +70 -0
  97. package/core/template-resolver.ts +156 -0
  98. package/core/triage.ts +26 -0
  99. package/core/verify.ts +15 -0
  100. package/core/wiki-native.ts +2423 -0
  101. package/core/wiki.ts +27 -0
  102. package/core/workflow-event-emitter.ts +163 -0
  103. package/core/workflows/engine.ts +273 -0
  104. package/core/workflows/expressions.ts +76 -0
  105. package/core/workflows/index.ts +38 -0
  106. package/core/workflows/steps/command.ts +43 -0
  107. package/core/workflows/steps/gate.ts +47 -0
  108. package/core/workflows/steps/gxpm.ts +44 -0
  109. package/core/workflows/steps/linear.ts +31 -0
  110. package/core/workflows/steps/shell.ts +65 -0
  111. package/core/workflows/types.ts +62 -0
  112. package/core/workspace-runtime.ts +227 -0
  113. package/core/worktree-init-steps.ts +647 -0
  114. package/core/worktree-init.ts +330 -0
  115. package/core/worktree-owner.ts +143 -0
  116. package/docs/GXPM_VERIFY.md +98 -0
  117. package/docs/INSTALL_FOR_AGENTS.md +113 -0
  118. package/docs/README.md +57 -0
  119. package/docs/adr/adr-005-multi-platform-skill-converter.md +72 -0
  120. package/docs/agents/domain.md +30 -0
  121. package/docs/agents/issue-tracker.md +30 -0
  122. package/docs/agents/triage-labels.md +32 -0
  123. package/docs/architecture/gxpm-architecture-diagram.md +265 -0
  124. package/docs/architecture/gxpm-current-architecture.md +175 -0
  125. package/docs/architecture/gxpm-current-flow.md +278 -0
  126. package/docs/architecture/gxpm-replacement-architecture.md +211 -0
  127. package/docs/architecture/gxpm-target-architecture.md +449 -0
  128. package/docs/architecture/gxpm-v0-contract.md +311 -0
  129. package/docs/architecture/layered-workflow-boundaries.md +193 -0
  130. package/docs/architecture/preset-system.md +126 -0
  131. package/docs/architecture/scaffold-northstar.md +23 -0
  132. package/docs/brainstorms/2026-05-14-bdd-then-tdd-design.md +320 -0
  133. package/docs/brainstorms/README.md +22 -0
  134. package/docs/brainstorms/docs-knowledge-system-requirements.md +29 -0
  135. package/docs/governance/beta-skill-promotion.md +39 -0
  136. package/docs/governance/development-contract.md +144 -0
  137. package/docs/governance/gherkin-style.md +90 -0
  138. package/docs/governance/host-adapter.md +56 -0
  139. package/docs/governance/skill-authoring.md +87 -0
  140. package/docs/governance/skill-testing.md +356 -0
  141. package/docs/governance/template-authoring.md +53 -0
  142. package/docs/migrations/v0.2.md +51 -0
  143. package/docs/plans/README.md +23 -0
  144. package/docs/plans/bdd-then-tdd-plan.md +1767 -0
  145. package/docs/plans/docs-knowledge-system-plan.md +31 -0
  146. package/docs/plans/spec-kit-sdd-adoption-plan.md +305 -0
  147. package/docs/research/agents-md-best-practices.md +207 -0
  148. package/docs/research/archon-study.md +351 -0
  149. package/docs/research/claude-hooks-study.md +440 -0
  150. package/docs/research/codex-hooks-study.md +624 -0
  151. package/docs/research/everything-claude-code-study.md +252 -0
  152. package/docs/research/from-skills-to-layered-workflow.md +322 -0
  153. package/docs/research/gsd-study.md +69 -0
  154. package/docs/research/kimi-hooks-study.md +274 -0
  155. package/docs/research/mattpocock-skills-comparison.md +429 -0
  156. package/docs/research/mattpocock-skills-study.md +275 -0
  157. package/docs/research/oh-my-codex-study.md +279 -0
  158. package/docs/research/perplexity-agent-skills-design.md +168 -0
  159. package/docs/research/pmc-gstack-skill-study.md +122 -0
  160. package/docs/research/spec-kit-study.md +224 -0
  161. package/docs/research/superpowers-study.md +209 -0
  162. package/docs/roadmap/initial-roadmap.md +53 -0
  163. package/docs/solutions/README.md +45 -0
  164. package/docs/solutions/artifact-nesting-recovery.md +58 -0
  165. package/docs/solutions/session-context-restore-practice.md +67 -0
  166. package/docs/solutions/workflow/version-drift-recovery.md +49 -0
  167. package/docs/solutions/worktree-gate-recovery.md +62 -0
  168. package/docs/specs/README.md +28 -0
  169. package/docs/specs/claude.md +45 -0
  170. package/docs/specs/codex.md +44 -0
  171. package/docs/specs/cursor.md +44 -0
  172. package/hosts/adapters/claude.ts +29 -0
  173. package/hosts/adapters/codex.ts +27 -0
  174. package/hosts/adapters/cursor.ts +27 -0
  175. package/hosts/adapters/kimi.ts +27 -0
  176. package/hosts/claude.ts +23 -0
  177. package/hosts/codex.ts +26 -0
  178. package/hosts/cursor.ts +19 -0
  179. package/hosts/index.ts +33 -0
  180. package/hosts/registry.test.ts +52 -0
  181. package/hosts/registry.ts +57 -0
  182. package/hosts/schema.ts +58 -0
  183. package/package.json +52 -0
  184. package/scripts/browser.ts +185 -0
  185. package/scripts/cleanup.ts +142 -0
  186. package/scripts/commands/artifact.ts +115 -0
  187. package/scripts/commands/autopilot.ts +143 -0
  188. package/scripts/commands/capability.ts +57 -0
  189. package/scripts/commands/config.ts +69 -0
  190. package/scripts/commands/dag.ts +126 -0
  191. package/scripts/commands/feedback.ts +123 -0
  192. package/scripts/commands/gate.ts +291 -0
  193. package/scripts/commands/helpers.ts +126 -0
  194. package/scripts/commands/hook.ts +66 -0
  195. package/scripts/commands/init.ts +515 -0
  196. package/scripts/commands/issue.ts +825 -0
  197. package/scripts/commands/phase.ts +61 -0
  198. package/scripts/commands/preset.ts +159 -0
  199. package/scripts/commands/runtime.ts +199 -0
  200. package/scripts/commands/specify.ts +71 -0
  201. package/scripts/commands/upgrade.ts +243 -0
  202. package/scripts/commands/verify.ts +183 -0
  203. package/scripts/commands/wiki.ts +242 -0
  204. package/scripts/commands/workflow.ts +131 -0
  205. package/scripts/dev-skill.ts +55 -0
  206. package/scripts/discover-skills.ts +116 -0
  207. package/scripts/doctor.ts +410 -0
  208. package/scripts/dogfood-check.ts +125 -0
  209. package/scripts/eval-functional.ts +218 -0
  210. package/scripts/eval.ts +246 -0
  211. package/scripts/gen-skill-docs.ts +201 -0
  212. package/scripts/global-discover.ts +217 -0
  213. package/scripts/governance-check.ts +75 -0
  214. package/scripts/gxpm-check.ts +12 -0
  215. package/scripts/gxpm.ts +216 -0
  216. package/scripts/host-config.ts +62 -0
  217. package/scripts/install-claude-hooks.ts +138 -0
  218. package/scripts/install-codex-hooks.ts +271 -0
  219. package/scripts/install-hooks.ts +128 -0
  220. package/scripts/install-kimi-hooks.ts +92 -0
  221. package/scripts/install-skill.ts +184 -0
  222. package/scripts/phase-artifact-commands.ts +100 -0
  223. package/scripts/post-land-sync.ts +46 -0
  224. package/scripts/scaffold-check.ts +85 -0
  225. package/scripts/skill-naming-check.ts +78 -0
  226. package/scripts/skill-structure-check.ts +157 -0
  227. package/scripts/skills-lock-check.ts +60 -0
  228. package/scripts/sync-markdown-artifacts.ts +172 -0
  229. package/scripts/uninstall.ts +162 -0
  230. package/scripts/version.ts +47 -0
  231. package/scripts/wait-pr-ready.ts +407 -0
  232. package/skills/gxpm/SKILL.md +485 -0
  233. package/skills/gxpm/SKILL.md.tmpl +422 -0
  234. package/skills/gxpm/references/CANON.md +53 -0
  235. package/skills/gxpm/references/key-rules.md +130 -0
  236. package/skills/gxpm-architecture/SKILL.md +106 -0
  237. package/skills/gxpm-architecture/references/DEEPENING.md +37 -0
  238. package/skills/gxpm-architecture/references/INTERFACE-DESIGN.md +44 -0
  239. package/skills/gxpm-autopilot/SKILL.md +116 -0
  240. package/skills/gxpm-autopilot/SKILL.md.tmpl +107 -0
  241. package/skills/gxpm-browser/SKILL.md +105 -0
  242. package/skills/gxpm-browser/SKILL.md.tmpl +41 -0
  243. package/skills/gxpm-browser/references/commands.md +43 -0
  244. package/skills/gxpm-browser/references/evidence-path.md +20 -0
  245. package/skills/gxpm-build/SKILL.md +78 -0
  246. package/skills/gxpm-cleanup/SKILL.md +76 -0
  247. package/skills/gxpm-debug-issue/SKILL.md +39 -0
  248. package/skills/gxpm-diagnose/SKILL.md +220 -0
  249. package/skills/gxpm-diagnose/SKILL.md.tmpl +31 -0
  250. package/skills/gxpm-diagnose/references/feedback-loop.md +34 -0
  251. package/skills/gxpm-diagnose/references/feedback-loops.md +43 -0
  252. package/skills/gxpm-diagnose/references/phases.md +60 -0
  253. package/skills/gxpm-eval/SKILL.md +78 -0
  254. package/skills/gxpm-explore-codebase/SKILL.md +36 -0
  255. package/skills/gxpm-explore-codebase/scripts/summarize-communities.ts +51 -0
  256. package/skills/gxpm-feedback/SKILL.md +122 -0
  257. package/skills/gxpm-grill/SKILL.md +159 -0
  258. package/skills/gxpm-grill/SKILL.md.tmpl +77 -0
  259. package/skills/gxpm-grill/references/documentation-templates.md +56 -0
  260. package/skills/gxpm-grill/references/process.md +25 -0
  261. package/skills/gxpm-handoff/SKILL.md +112 -0
  262. package/skills/gxpm-hygiene/SKILL.md +69 -0
  263. package/skills/gxpm-implementer/SKILL.md +142 -0
  264. package/skills/gxpm-implementer/SKILL.md.tmpl +141 -0
  265. package/skills/gxpm-linear/SKILL.md +282 -0
  266. package/skills/gxpm-linear/SKILL.md.tmpl +86 -0
  267. package/skills/gxpm-linear/references/commands.md +75 -0
  268. package/skills/gxpm-linear/references/workflows.md +120 -0
  269. package/skills/gxpm-planning/SKILL.md +134 -0
  270. package/skills/gxpm-prototype/SKILL.md +64 -0
  271. package/skills/gxpm-refactor-safely/SKILL.md +62 -0
  272. package/skills/gxpm-review-army/SKILL.md +117 -0
  273. package/skills/gxpm-review-changes/SKILL.md +36 -0
  274. package/skills/gxpm-setup/SKILL.md +101 -0
  275. package/skills/gxpm-specifier/SKILL.md +135 -0
  276. package/skills/gxpm-tdd/SKILL.md +187 -0
  277. package/skills/gxpm-tdd/references/interface-design.md +23 -0
  278. package/skills/gxpm-tdd/references/mocking.md +27 -0
  279. package/skills/gxpm-tdd/references/red-green-refactor.md +61 -0
  280. package/skills/gxpm-tdd/references/troubleshooting.md +28 -0
  281. package/skills/gxpm-tdd/references/workflow.md +50 -0
  282. package/skills/gxpm-tdd/testing-anti-patterns.tmpl +304 -0
  283. package/skills/gxpm-triage/SKILL.md +160 -0
  284. package/skills/gxpm-verify/SKILL.md +107 -0
  285. package/skills/gxpm-write-skill/SKILL.md +131 -0
  286. package/skills/gxpm-zoom-out/SKILL.md +69 -0
  287. package/skills/maintain-hygiene-skills-lock/SKILL.md +54 -0
  288. package/skills/maintain-hygiene-skills-lock/SKILL.md.tmpl +53 -0
  289. package/templates/constitution-template.md +63 -0
  290. package/templates/hooks/gxpm-commit-msg +16 -0
  291. package/templates/hooks/gxpm-post-checkout +19 -0
  292. package/templates/hooks/gxpm-post-commit +7 -0
  293. package/templates/hooks/gxpm-post-merge +29 -0
  294. package/templates/hooks/gxpm-pre-commit +39 -0
  295. package/templates/hooks/gxpm-pre-push +33 -0
  296. package/templates/plan-template.md.tmpl +46 -0
  297. package/templates/spec-template.md.tmpl +63 -0
  298. package/templates/specify-stub.tmpl +22 -0
  299. package/templates/tasks-template.md.tmpl +32 -0
@@ -0,0 +1,62 @@
1
+ import { existsSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { writeArtifact } from "./artifacts";
4
+ import { lintCodexPlans } from "./plan-lint";
5
+ import { readIssueState } from "./state";
6
+ import { AgentRegistry } from "./agent-runtime";
7
+
8
+ export function initializeSelfReview(input: { root?: string; issueId: string; army?: boolean }) {
9
+ const root = input.root ?? process.cwd();
10
+ const state = readIssueState({ root, issueId: input.issueId });
11
+ if (state.currentPhase !== "ac-check") {
12
+ throw new Error(`Self review can only be initialized from ac-check phase: current phase is ${state.currentPhase}`);
13
+ }
14
+
15
+ const result = writeArtifact({
16
+ root,
17
+ issueId: input.issueId,
18
+ type: "self-review",
19
+ payload: {
20
+ findings: [],
21
+ reviewedArtifacts: ["acceptance-check", "local-verify"],
22
+ risks: [],
23
+ status: "draft",
24
+ summary: "",
25
+ plan_lint_findings: lintCodexPlans(root, input.issueId),
26
+ },
27
+ });
28
+
29
+ // When army mode is enabled, also initialize the review-report artifact
30
+ if (input.army) {
31
+ const registry = new AgentRegistry();
32
+ // Discover from the project root (where agents/ directory lives)
33
+ // Fallback to process.cwd() if root doesn't contain agents/
34
+ const discoverRoot = existsSync(join(root, "agents")) ? root : process.cwd();
35
+ registry.discover(discoverRoot);
36
+ const agents = registry.listByArmy("review-army");
37
+
38
+ if (agents.length === 0) {
39
+ console.warn("warning: --army flag set but no review-army agents found");
40
+ } else {
41
+ // Write an initial review-report with empty findings
42
+ // The actual execution happens via the host's subagent mechanism
43
+ writeArtifact({
44
+ root,
45
+ issueId: input.issueId,
46
+ type: "review-report",
47
+ payload: {
48
+ army: "review-army",
49
+ phase: "self-review",
50
+ issueId: input.issueId,
51
+ generatedAt: new Date().toISOString(),
52
+ findings: [],
53
+ summary: `${agents.length} review role(s) queued for execution: ${agents.map((a) => a.name).join(", ")}`,
54
+ status: "draft",
55
+ agents: agents.map((a) => ({ name: a.name, role: a.role, description: a.description })),
56
+ },
57
+ });
58
+ }
59
+ }
60
+
61
+ return result;
62
+ }
@@ -0,0 +1,70 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { homedir, tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { randomUUID } from "node:crypto";
5
+ import { getConfigValue } from "./config";
6
+
7
+ let volatileSessionId: string | null = null;
8
+
9
+ export function resolveSessionId(env: NodeJS.ProcessEnv = process.env): string {
10
+ const codexSessionId = env.CODEX_COMPANION_SESSION_ID?.trim();
11
+ if (codexSessionId) {
12
+ return `codex:${codexSessionId}`;
13
+ }
14
+
15
+ const cmuxSurfaceId = env.CMUX_SURFACE_ID?.trim();
16
+ if (cmuxSurfaceId) {
17
+ return `cmux:${cmuxSurfaceId}`;
18
+ }
19
+
20
+ return `gen:${readOrCreateGeneratedSessionId(env)}`;
21
+ }
22
+
23
+ export interface AgentIdentity {
24
+ host: string;
25
+ sessionId: string;
26
+ actor: string;
27
+ }
28
+
29
+ export function resolveAgentIdentity(
30
+ env: NodeJS.ProcessEnv = process.env,
31
+ root?: string,
32
+ ): AgentIdentity {
33
+ const sessionId = resolveSessionId(env);
34
+ const host = sessionId.split(":")[0] ?? "gen";
35
+ const envName = env.GXPM_AGENT_NAME?.trim() ?? "";
36
+ const configName = root ? String(getConfigValue({ root, key: "agent.name" }).value ?? "").trim() : "";
37
+ const actor = envName || configName || host;
38
+ return { host, sessionId, actor };
39
+ }
40
+
41
+ function readOrCreateGeneratedSessionId(env: NodeJS.ProcessEnv): string {
42
+ for (const cachePath of candidateCachePaths(env)) {
43
+ try {
44
+ if (existsSync(cachePath)) {
45
+ const cached = readFileSync(cachePath, "utf8").trim();
46
+ if (cached) {
47
+ return cached;
48
+ }
49
+ }
50
+
51
+ const generated = randomUUID();
52
+ mkdirSync(join(cachePath, ".."), { recursive: true });
53
+ writeFileSync(cachePath, `${generated}\n`);
54
+ return generated;
55
+ } catch {}
56
+ }
57
+
58
+ volatileSessionId ??= randomUUID();
59
+ return volatileSessionId;
60
+ }
61
+
62
+ function candidateCachePaths(env: NodeJS.ProcessEnv): string[] {
63
+ const paths = new Set<string>();
64
+ const home = env.HOME?.trim() || homedir();
65
+ if (home) {
66
+ paths.add(join(home, ".gxpm", "session-id"));
67
+ }
68
+ paths.add(join(tmpdir(), "gxpm", "session-id"));
69
+ return [...paths];
70
+ }
package/core/ship.ts ADDED
@@ -0,0 +1,86 @@
1
+ import { existsSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { writeArtifact } from "./artifacts";
4
+ import { readIssueState } from "./state";
5
+ import { AgentRegistry } from "./agent-runtime";
6
+
7
+ export function initializeShipReadiness(input: { root?: string; issueId: string; army?: boolean }) {
8
+ const root = input.root ?? process.cwd();
9
+ const state = readIssueState({ root, issueId: input.issueId });
10
+ if (state.currentPhase !== "cleanup" && state.currentPhase !== "self-review") {
11
+ throw new Error(`Ship readiness can only be initialized from cleanup or self-review phase: current phase is ${state.currentPhase}`);
12
+ }
13
+
14
+ const result = writeArtifact({
15
+ root,
16
+ issueId: input.issueId,
17
+ type: "ship-readiness",
18
+ payload: {
19
+ checklist: [],
20
+ compatibilityMigration: {
21
+ backwardCompatible: true,
22
+ configChanges: false,
23
+ migrationSteps: "",
24
+ },
25
+ blastRadius: {
26
+ affectedSubsystems: [],
27
+ guardrails: "",
28
+ unintendedEffects: "",
29
+ },
30
+ humanVerification: {
31
+ edgeCases: "",
32
+ notVerified: "",
33
+ verifiedScenarios: "",
34
+ },
35
+ releaseNotes: "",
36
+ reviewedArtifacts: ["self-review", "acceptance-check"],
37
+ risks: [],
38
+ risksAndMitigations: [],
39
+ rollbackPlan: {
40
+ failureSymptoms: "",
41
+ featureFlags: "",
42
+ rollbackCommand: "",
43
+ },
44
+ securityImpact: {
45
+ fileSystemAccessChanged: false,
46
+ networkCallsChanged: false,
47
+ newPermissionsOrCapabilities: false,
48
+ riskAndMitigation: "",
49
+ secretsHandlingChanged: false,
50
+ },
51
+ status: "draft",
52
+ summary: "",
53
+ targetBranch: "",
54
+ },
55
+ });
56
+
57
+ // When army mode is enabled, also initialize the ship-audit-report artifact
58
+ if (input.army) {
59
+ const registry = new AgentRegistry();
60
+ const discoverRoot = existsSync(join(root, "agents")) ? root : process.cwd();
61
+ registry.discover(discoverRoot);
62
+ const agents = registry.listByArmy("ship-audit-army");
63
+
64
+ if (agents.length === 0) {
65
+ console.warn("warning: --army flag set but no ship-audit-army agents found");
66
+ } else {
67
+ writeArtifact({
68
+ root,
69
+ issueId: input.issueId,
70
+ type: "ship-audit-report",
71
+ payload: {
72
+ army: "ship-audit-army",
73
+ phase: "ship",
74
+ issueId: input.issueId,
75
+ generatedAt: new Date().toISOString(),
76
+ findings: [],
77
+ summary: `${agents.length} audit role(s) queued for execution: ${agents.map((a) => a.name).join(", ")}`,
78
+ status: "draft",
79
+ agents: agents.map((a) => ({ name: a.name, role: a.role, description: a.description })),
80
+ },
81
+ });
82
+ }
83
+ }
84
+
85
+ return result;
86
+ }
@@ -0,0 +1,173 @@
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { execSync } from "node:child_process";
4
+ import { writeArtifact } from "./artifacts";
5
+ import { readIssueState } from "./state";
6
+ import { resolveAgentIdentity } from "./session";
7
+ import { BehaviorSpecSchema, type BehaviorSpec } from "./contracts/behavior-spec.schema";
8
+
9
+ interface SpecifyInput {
10
+ root?: string;
11
+ issueId: string;
12
+ }
13
+
14
+ export function initializeSpecify(input: SpecifyInput) {
15
+ const state = readIssueState({ root: input.root, issueId: input.issueId });
16
+ if (state.currentPhase !== "specify") {
17
+ throw new Error(
18
+ `Specify can only be initialized from specify phase: current phase is ${state.currentPhase}`,
19
+ );
20
+ }
21
+
22
+ const now = new Date().toISOString();
23
+ const identity = resolveAgentIdentity();
24
+
25
+ const payload = {
26
+ $schema: "behavior-spec.v1",
27
+ issueId: input.issueId,
28
+ createdAt: now,
29
+ createdBy: identity.actor,
30
+ confirmedAt: null,
31
+ confirmedBy: null,
32
+ feature: {
33
+ title: "<placeholder>",
34
+ asA: "<placeholder>",
35
+ iWant: "<placeholder>",
36
+ soThat: "<placeholder>",
37
+ },
38
+ scenarios: [
39
+ {
40
+ id: "scn-01",
41
+ name: "<placeholder>",
42
+ given: ["<placeholder>"],
43
+ when: "<placeholder>",
44
+ then: ["<placeholder>"],
45
+ examples: [],
46
+ stubPath: "<placeholder>",
47
+ },
48
+ ],
49
+ guidelinesRef: "docs/governance/gherkin-style.md@v1",
50
+ };
51
+
52
+ return writeArtifact({
53
+ root: input.root,
54
+ issueId: input.issueId,
55
+ type: "behavior-spec",
56
+ payload,
57
+ });
58
+ }
59
+
60
+ // ---------------------------------------------------------------------------
61
+ // Helpers
62
+ // ---------------------------------------------------------------------------
63
+
64
+ interface ConfirmInput {
65
+ root?: string;
66
+ issueId: string;
67
+ confirmedBy?: string;
68
+ }
69
+
70
+ function gitUserEmail(): string {
71
+ try {
72
+ return execSync("git config user.email", { encoding: "utf8" }).trim();
73
+ } catch {
74
+ return "unknown@local";
75
+ }
76
+ }
77
+
78
+ function specArtifactPath(root: string, issueId: string): string {
79
+ return join(root, ".gxpm", "issues", issueId, "artifacts", "behavior-spec.json");
80
+ }
81
+
82
+ function readStoredSpec(root: string, issueId: string): { stored: { payload: BehaviorSpec; [key: string]: unknown } } {
83
+ const path = specArtifactPath(root, issueId);
84
+ if (!existsSync(path)) {
85
+ throw new Error(
86
+ `behavior-spec.json not found for ${issueId}; run \`gxpm specify init ${issueId}\` first`,
87
+ );
88
+ }
89
+ const stored = JSON.parse(readFileSync(path, "utf8"));
90
+ BehaviorSpecSchema.parse(stored.payload);
91
+ return { stored };
92
+ }
93
+
94
+ const PLACEHOLDER_SENTINEL = "<placeholder>";
95
+
96
+ function findRemainingPlaceholders(spec: BehaviorSpec): string[] {
97
+ const violations: string[] = [];
98
+ const f = spec.feature;
99
+ for (const [key, val] of Object.entries(f)) {
100
+ if (val === PLACEHOLDER_SENTINEL) violations.push(`feature.${key}`);
101
+ }
102
+ spec.scenarios.forEach((scn, idx) => {
103
+ const tag = `scenarios[${idx}]`;
104
+ if (scn.name === PLACEHOLDER_SENTINEL) violations.push(`${tag}.name`);
105
+ if (scn.when === PLACEHOLDER_SENTINEL) violations.push(`${tag}.when`);
106
+ if (scn.stubPath === PLACEHOLDER_SENTINEL) violations.push(`${tag}.stubPath`);
107
+ scn.given.forEach((g, i) => {
108
+ if (g === PLACEHOLDER_SENTINEL) violations.push(`${tag}.given[${i}]`);
109
+ });
110
+ scn.then.forEach((t, i) => {
111
+ if (t === PLACEHOLDER_SENTINEL) violations.push(`${tag}.then[${i}]`);
112
+ });
113
+ });
114
+ return violations;
115
+ }
116
+
117
+ // ---------------------------------------------------------------------------
118
+ // Exported functions
119
+ // ---------------------------------------------------------------------------
120
+
121
+ export function confirmSpecify(input: ConfirmInput) {
122
+ const root = input.root ?? process.cwd();
123
+ const { stored } = readStoredSpec(root, input.issueId);
124
+ const spec = stored.payload;
125
+
126
+ const placeholders = findRemainingPlaceholders(spec);
127
+ if (placeholders.length > 0) {
128
+ throw new Error(
129
+ `Cannot confirm: <placeholder> sentinels remain in: ${placeholders.join(", ")}`,
130
+ );
131
+ }
132
+
133
+ for (const scn of spec.scenarios) {
134
+ const file = scn.stubPath.split(":")[0];
135
+ const abs = join(root, file);
136
+ if (!existsSync(abs)) {
137
+ throw new Error(`Stub file missing: ${file} (scenario ${scn.id})`);
138
+ }
139
+ }
140
+
141
+ const now = new Date().toISOString();
142
+ const confirmedBy = input.confirmedBy ?? gitUserEmail();
143
+ stored.payload.confirmedAt = now;
144
+ stored.payload.confirmedBy = confirmedBy;
145
+ writeFileSync(
146
+ specArtifactPath(root, input.issueId),
147
+ `${JSON.stringify(stored, null, 2)}\n`,
148
+ );
149
+ }
150
+
151
+ export function reviseSpecify(input: { root?: string; issueId: string }) {
152
+ const root = input.root ?? process.cwd();
153
+ // Revise must be schema-tolerant: its purpose is to restore a confirmable state.
154
+ // We deliberately skip BehaviorSpecSchema.parse() so a spec that was edited into
155
+ // an inconsistent shape (e.g. confirmedAt set but confirmedBy null, or partial
156
+ // field types) can still be reset to draft. The next confirmSpecify call will
157
+ // re-validate against the full schema.
158
+ const path = specArtifactPath(root, input.issueId);
159
+ if (!existsSync(path)) {
160
+ throw new Error(
161
+ `behavior-spec.json not found for ${input.issueId}; run \`gxpm specify init ${input.issueId}\` first`,
162
+ );
163
+ }
164
+ const stored = JSON.parse(readFileSync(path, "utf8"));
165
+ if (!stored?.payload || typeof stored.payload !== "object") {
166
+ throw new Error(
167
+ `behavior-spec.json is malformed (missing payload) for ${input.issueId}; cannot revise`,
168
+ );
169
+ }
170
+ stored.payload.confirmedAt = null;
171
+ stored.payload.confirmedBy = null;
172
+ writeFileSync(path, `${JSON.stringify(stored, null, 2)}\n`);
173
+ }