@davidorex/pi-workflows 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 (115) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +169 -0
  3. package/agents/audit-fixer.agent.yaml +18 -0
  4. package/agents/code-explorer.agent.yaml +11 -0
  5. package/agents/decomposer.agent.yaml +51 -0
  6. package/agents/investigator.agent.yaml +49 -0
  7. package/agents/pattern-analyzer.agent.yaml +21 -0
  8. package/agents/phase-author.agent.yaml +16 -0
  9. package/agents/plan-decomposer.agent.yaml +12 -0
  10. package/agents/quality-analyzer.agent.yaml +21 -0
  11. package/agents/researcher.agent.yaml +38 -0
  12. package/agents/spec-implementer.agent.yaml +12 -0
  13. package/agents/structure-analyzer.agent.yaml +21 -0
  14. package/agents/synthesizer.agent.yaml +23 -0
  15. package/agents/verifier.agent.yaml +12 -0
  16. package/package.json +40 -0
  17. package/schemas/decomposition-specs.schema.json +50 -0
  18. package/schemas/execution-results.schema.json +50 -0
  19. package/schemas/investigation-findings.schema.json +45 -0
  20. package/schemas/pattern-analysis.schema.json +43 -0
  21. package/schemas/plan-breakdown.schema.json +22 -0
  22. package/schemas/quality-analysis.schema.json +35 -0
  23. package/schemas/research-findings.schema.json +44 -0
  24. package/schemas/structure-analysis.schema.json +42 -0
  25. package/schemas/synthesis.schema.json +33 -0
  26. package/schemas/verifier-output.schema.json +96 -0
  27. package/src/agent-spec.test.ts +289 -0
  28. package/src/agent-spec.ts +122 -0
  29. package/src/block-validation.test.ts +350 -0
  30. package/src/checkpoint.test.ts +237 -0
  31. package/src/checkpoint.ts +139 -0
  32. package/src/completion.test.ts +143 -0
  33. package/src/completion.ts +68 -0
  34. package/src/dag.test.ts +350 -0
  35. package/src/dag.ts +219 -0
  36. package/src/dispatch.test.ts +473 -0
  37. package/src/dispatch.ts +353 -0
  38. package/src/expression.test.ts +353 -0
  39. package/src/expression.ts +332 -0
  40. package/src/format.test.ts +80 -0
  41. package/src/format.ts +41 -0
  42. package/src/graduated-failure.test.ts +556 -0
  43. package/src/index.test.ts +114 -0
  44. package/src/index.ts +488 -0
  45. package/src/loop.test.ts +549 -0
  46. package/src/output.test.ts +213 -0
  47. package/src/output.ts +70 -0
  48. package/src/parallel-integration.test.ts +175 -0
  49. package/src/resume.test.ts +192 -0
  50. package/src/state.test.ts +192 -0
  51. package/src/state.ts +253 -0
  52. package/src/step-agent.test.ts +327 -0
  53. package/src/step-agent.ts +178 -0
  54. package/src/step-command.test.ts +330 -0
  55. package/src/step-command.ts +132 -0
  56. package/src/step-foreach.test.ts +647 -0
  57. package/src/step-foreach.ts +148 -0
  58. package/src/step-gate.test.ts +185 -0
  59. package/src/step-gate.ts +116 -0
  60. package/src/step-loop.test.ts +626 -0
  61. package/src/step-loop.ts +323 -0
  62. package/src/step-parallel.test.ts +475 -0
  63. package/src/step-parallel.ts +168 -0
  64. package/src/step-pause.test.ts +30 -0
  65. package/src/step-pause.ts +27 -0
  66. package/src/step-shared.test.ts +355 -0
  67. package/src/step-shared.ts +157 -0
  68. package/src/step-transform.test.ts +155 -0
  69. package/src/step-transform.ts +47 -0
  70. package/src/template-integration.test.ts +72 -0
  71. package/src/template.test.ts +162 -0
  72. package/src/template.ts +92 -0
  73. package/src/test-helpers.ts +51 -0
  74. package/src/tui.test.ts +355 -0
  75. package/src/tui.ts +182 -0
  76. package/src/types.ts +195 -0
  77. package/src/verifier-schema.test.ts +226 -0
  78. package/src/workflow-discovery.test.ts +105 -0
  79. package/src/workflow-discovery.ts +113 -0
  80. package/src/workflow-executor.test.ts +1530 -0
  81. package/src/workflow-executor.ts +810 -0
  82. package/src/workflow-sdk.test.ts +238 -0
  83. package/src/workflow-sdk.ts +262 -0
  84. package/src/workflow-spec.test.ts +576 -0
  85. package/src/workflow-spec.ts +471 -0
  86. package/templates/analyzers/base-analyzer.md +9 -0
  87. package/templates/analyzers/macros.md +1 -0
  88. package/templates/analyzers/patterns-task.md +11 -0
  89. package/templates/analyzers/patterns.md +15 -0
  90. package/templates/analyzers/quality-task.md +11 -0
  91. package/templates/analyzers/quality.md +15 -0
  92. package/templates/analyzers/structure-task.md +17 -0
  93. package/templates/analyzers/structure.md +15 -0
  94. package/templates/audit-fixer/task.md +67 -0
  95. package/templates/decomposer/task.md +56 -0
  96. package/templates/explorer/system.md +3 -0
  97. package/templates/explorer/task.md +9 -0
  98. package/templates/investigator/task.md +30 -0
  99. package/templates/phase-author/task.md +74 -0
  100. package/templates/plan-decomposer/task.md +46 -0
  101. package/templates/researcher/task.md +26 -0
  102. package/templates/spec-implementer/task.md +53 -0
  103. package/templates/synthesizer/system.md +3 -0
  104. package/templates/synthesizer/task.md +38 -0
  105. package/templates/verifier/task.md +57 -0
  106. package/workflows/create-phase.workflow.yaml +67 -0
  107. package/workflows/do-gap.workflow.yaml +153 -0
  108. package/workflows/fix-audit.workflow.yaml +217 -0
  109. package/workflows/gap-to-phase.workflow.yaml +98 -0
  110. package/workflows/parallel-analysis.workflow.yaml +62 -0
  111. package/workflows/parallel-explicit.workflow.yaml +42 -0
  112. package/workflows/pausable-analysis.workflow.yaml +44 -0
  113. package/workflows/resumable-analysis.workflow.yaml +42 -0
  114. package/workflows/self-implement.workflow.yaml +63 -0
  115. package/workflows/typed-analysis.workflow.yaml +63 -0
@@ -0,0 +1,56 @@
1
+ ## Decomposition Request
2
+
3
+ **Gap:** {{ gap.id }} — {{ gap.description }}
4
+
5
+ ## Investigation Findings
6
+
7
+ **Complexity:** {{ investigation.complexity }}
8
+ **Summary:** {{ investigation.summary }}
9
+
10
+ ### Affected Files
11
+ {% for f in investigation.affected_files %}
12
+ - `{{ f.path }}` — {{ f.role }}: {{ f.current_behavior }}
13
+ {% endfor %}
14
+
15
+ ### Constraints
16
+ {% for c in investigation.constraints %}
17
+ - {{ c }}
18
+ {% endfor %}
19
+
20
+ ### Risks
21
+ {% for r in investigation.risks %}
22
+ - {{ r }}
23
+ {% endfor %}
24
+
25
+ {% if research %}
26
+ ## Research Findings
27
+
28
+ ### Patterns
29
+ {% for p in research.patterns %}
30
+ - **{{ p.name }}**: {{ p.description }} — {{ p.applicability }}
31
+ {% endfor %}
32
+
33
+ ### Recommendations
34
+ {% for r in research.recommendations %}
35
+ - {{ r }}
36
+ {% endfor %}
37
+ {% endif %}
38
+
39
+ ## Instructions
40
+
41
+ Decompose this gap into implementation specs. Each spec is a unit of work for a single implementing agent.
42
+
43
+ 1. Break the work into the smallest meaningful units
44
+ 2. Declare file targets for each spec
45
+ 3. Set `depends_on` for specs that must be sequential (shared files, interface dependencies)
46
+ 4. Mark `parallel_safe` for specs that can run concurrently
47
+ 5. Write acceptance criteria that are verifiable (grep patterns, test commands, not "it works")
48
+ 6. Estimate complexity per spec
49
+
50
+ ## Required Output Schema
51
+
52
+ You MUST produce JSON conforming exactly to this schema. Every required field must be present.
53
+
54
+ ```json
55
+ {{ output_schema }}
56
+ ```
@@ -0,0 +1,3 @@
1
+ You are a code explorer. Given a path, produce a comprehensive structural overview as JSON.
2
+
3
+ Include files with their sizes, exports, imports, and line counts. Identify types, interfaces, and key functions. Map dependencies between files.
@@ -0,0 +1,9 @@
1
+ Explore the code at `{{ path }}`.
2
+
3
+ Produce a JSON object with:
4
+ - `files`: array of { path, type, language, lines, exports, imports }
5
+ - `types`: array of { name, file, line, kind, definition }
6
+ - `dependencies`: array of { from, to, type }
7
+ - `entryPoints`: array of file paths that serve as entry points
8
+
9
+ Write your findings as valid JSON.
@@ -0,0 +1,30 @@
1
+ ## Gap to Investigate
2
+
3
+ **ID:** {{ gap.id }}
4
+ **Description:** {{ gap.description }}
5
+ **Category:** {{ gap.category }}
6
+ **Priority:** {{ gap.priority }}
7
+ {% if gap.details %}
8
+ **Details:** {{ gap.details }}
9
+ {% endif %}
10
+
11
+ ## Instructions
12
+
13
+ Produce structured findings for this gap. Scope your investigation to the minimum needed:
14
+
15
+ 1. If the gap names specific files, read those. If not, use `find` or `ls` to identify candidates — don't read file contents unless you need to confirm behavior.
16
+ 2. For each affected file: what it does, what needs to change.
17
+ 3. List hard constraints (existing tests, interfaces, conventions that must not break).
18
+ 4. Note risks only if non-obvious.
19
+ 5. Set `needs_research` to true only if the solution requires knowledge you don't have. Most gaps don't.
20
+ 6. Complexity: `low` = single concern, mechanical. `medium` = multiple files, design choices. `high` = architectural.
21
+
22
+ Match your depth to the gap's category and priority. A `cleanup/low` gap needs a few lines of findings, not a codebase survey.
23
+
24
+ ## Required Output Schema
25
+
26
+ You MUST produce JSON conforming exactly to this schema. Every required field must be present.
27
+
28
+ ```json
29
+ {{ output_schema }}
30
+ ```
@@ -0,0 +1,74 @@
1
+ ## Intent
2
+
3
+ {{ intent }}
4
+
5
+ ## Existing Phases
6
+
7
+ {% for phase in phases %}
8
+ - Phase {{ phase.number }}: {{ phase.name }} ({{ phase.status }})
9
+ {% endfor %}
10
+
11
+ ## Current Architecture
12
+
13
+ {% for module in architecture.modules %}
14
+ - **{{ module.name }}** ({{ module.file }}, {{ module.lines }} lines): {{ module.responsibility }}
15
+ {% endfor %}
16
+
17
+ {% if architecture.compilation_pipeline %}
18
+ ### Compilation Pipeline
19
+ {% for stage in architecture.compilation_pipeline %}
20
+ {{ loop.index }}. **{{ stage.stage }}**: {{ stage.description }}
21
+ {% endfor %}
22
+ {% endif %}
23
+
24
+ ## Conventions
25
+
26
+ {% for rule in conventions.rules %}
27
+ - {{ rule.id }}: {{ rule.description }} ({{ rule.enforcement }})
28
+ {% endfor %}
29
+
30
+ ## Current Gaps
31
+
32
+ {% for gap in gaps %}
33
+ {% if gap.status == "open" %}
34
+ - [{{ gap.priority }}] {{ gap.id }}: {{ gap.description }}
35
+ {% endif %}
36
+ {% endfor %}
37
+
38
+ ## Current Inventory
39
+
40
+ - Step types: {{ inventory.step_types | length }}
41
+ - Agent specs: {{ inventory.agent_specs | length }}
42
+ - Schemas: {{ inventory.schemas | length }}
43
+ - Tests: {{ inventory.test_count }}
44
+
45
+ ## Instructions
46
+
47
+ Convert the intent above into a structured phase spec. Read the codebase to understand what exists and what the intent requires.
48
+
49
+ 1. **Determine the phase number** — next after the highest existing phase number
50
+ 2. **Write a clear intent statement** — what this phase accomplishes and why
51
+ 3. **Define success criteria** — each with a verify_method:
52
+ - `command`: can be verified by running a shell command (test suite, grep, validate)
53
+ - `inspect`: requires reading files and assessing content
54
+ - `human`: requires human judgment
55
+ 4. **Decompose into specs** — each spec is a focused unit of work:
56
+ - Sequential numbering continuing from the last spec in the previous phase
57
+ - Clear intent per spec
58
+ - Concrete acceptance criteria (strings, each independently checkable)
59
+ - Specs should be ordered by dependency — earlier specs don't depend on later ones
60
+ 5. **Identify dependencies** — which phase numbers this phase depends on
61
+ 6. **List artifacts produced** — file paths this phase will create or modify
62
+
63
+ ### Sizing guidance
64
+
65
+ - Each spec should be completable by one agent in one context window
66
+ - If a spec needs more than ~5 files changed, split it
67
+ - If a spec has more than ~5 acceptance criteria, it might be doing too much
68
+ - Group related changes (e.g., schema + instance + validation test = one spec)
69
+
70
+ ### Cross-reference gaps
71
+
72
+ Check if any existing open gaps are addressed by this phase. If so, note that in the spec intent — the gap can be marked resolved when the spec completes.
73
+
74
+ Produce a JSON object conforming to the phase schema.
@@ -0,0 +1,46 @@
1
+ ## Phase: {{ phase.name }}
2
+
3
+ ### Intent
4
+ {{ phase.intent }}
5
+
6
+ ### Success Criteria
7
+ {% for criterion in phase.success_criteria %}
8
+ - {{ criterion.criterion }} (verify: {{ criterion.verify_method }})
9
+ {% endfor %}
10
+
11
+ ### Specs
12
+ {% if phase.specs %}
13
+ {% for spec in phase.specs %}
14
+ - **{{ spec.id }}**: {{ spec.name }}
15
+ {% endfor %}
16
+ {% endif %}
17
+
18
+ ### Current Architecture
19
+ {% for module in architecture.modules %}
20
+ - **{{ module.name }}** ({{ module.file }}): {{ module.responsibility }}
21
+ {% endfor %}
22
+
23
+ ### Conventions
24
+ {% for rule in conventions.rules %}
25
+ - {{ rule.id }}: {{ rule.description }} ({{ rule.enforcement }})
26
+ {% endfor %}
27
+
28
+ ## Instructions
29
+
30
+ Decompose this phase into implementation plans. Each plan should be a focused unit of work that one agent can complete in a single context window.
31
+
32
+ Before creating plans:
33
+ 1. Read source files referenced in the architecture to understand existing patterns
34
+ 2. Identify logical boundaries — which specs or features are independent
35
+ 3. Determine which plans can run in parallel (independent file sets)
36
+
37
+ For each plan, produce:
38
+ - **name**: short identifier for the plan
39
+ - **intent**: what this plan accomplishes and why
40
+ - **tasks**: concrete list of things to do (create files, add tests, wire up)
41
+ - **files_to_change**: paths that will be created or modified
42
+ - **acceptance_criteria**: how to know the plan is done
43
+ - **context_needed**: files or modules the implementing agent should read first
44
+ - **parallel_group**: group name — plans in the same group can run concurrently (independent file sets get the same group; dependent plans get different groups)
45
+
46
+ Output a JSON object with a `plans` array conforming to the plan-breakdown schema.
@@ -0,0 +1,26 @@
1
+ ## Research Request
2
+
3
+ **Gap:** {{ gap.id }} — {{ gap.description }}
4
+
5
+ ## Questions
6
+
7
+ {% for q in research_questions %}
8
+ {{ loop.index }}. {{ q }}
9
+ {% endfor %}
10
+
11
+ ## Instructions
12
+
13
+ For each question:
14
+ 1. Answer based on known software engineering patterns, best practices, and established solutions
15
+ 2. Rate your confidence: `high` (well-known, widely used), `medium` (established but context-dependent), `low` (best guess)
16
+ 3. List sources where possible — documentation, known projects, pattern names
17
+
18
+ Also identify applicable patterns and concrete recommendations.
19
+
20
+ ## Required Output Schema
21
+
22
+ You MUST produce JSON conforming exactly to this schema. Every required field must be present.
23
+
24
+ ```json
25
+ {{ output_schema }}
26
+ ```
@@ -0,0 +1,53 @@
1
+ ## Plan: {{ plan.name }}
2
+
3
+ ### Intent
4
+ {{ plan.intent }}
5
+
6
+ ### Tasks
7
+ {% for task in plan.tasks %}
8
+ {{ loop.index }}. {{ task }}
9
+ {% endfor %}
10
+
11
+ ### Files to Change
12
+ {% for f in plan.files_to_change %}
13
+ - `{{ f }}`
14
+ {% endfor %}
15
+
16
+ ### Acceptance Criteria
17
+ {% for criterion in plan.acceptance_criteria %}
18
+ - {{ criterion }}
19
+ {% endfor %}
20
+
21
+ {% if plan.context_needed %}
22
+ ### Context to Read First
23
+ {% for ctx in plan.context_needed %}
24
+ - `{{ ctx }}`
25
+ {% endfor %}
26
+ {% endif %}
27
+
28
+ ### Architecture Reference
29
+ {% for module in architecture.modules %}
30
+ - **{{ module.name }}** ({{ module.file }}): {{ module.responsibility }}
31
+ {% endfor %}
32
+
33
+ ### Conventions
34
+ {% for rule in conventions.rules %}
35
+ - {{ rule.id }}: {{ rule.description }} ({{ rule.enforcement }})
36
+ {% endfor %}
37
+
38
+ ## Instructions
39
+
40
+ Implement this plan. Follow these steps:
41
+
42
+ 1. **Read context first**: Read the files listed under "Context to Read First" and any related source files to understand existing patterns
43
+ 2. **Implement each task**: Work through the task list in order, writing code that follows the project conventions
44
+ 3. **Run tests**: After implementation, run the relevant test suite to verify your changes work
45
+ 4. **Validate acceptance criteria**: Confirm each acceptance criterion is met
46
+
47
+ Produce a JSON result conforming to the execution-results schema with:
48
+ - **status**: "complete", "partial", or "failed"
49
+ - **tasks**: array of task results with name, status, files_modified
50
+ - **decisions**: any design decisions made during implementation
51
+ - **issues**: any problems encountered (with severity)
52
+ - **test_count**: number of tests passing after your changes
53
+ - **commit_hash**: empty string (commits are handled externally)
@@ -0,0 +1,3 @@
1
+ You are a report synthesizer. You receive typed analysis data from three independent reviewers (structure, quality, patterns) and produce a unified synthesis as structured JSON.
2
+
3
+ Every finding must trace to specific data from the input analyses. Do not invent findings that aren't grounded in the input data.
@@ -0,0 +1,38 @@
1
+ Synthesize the following analyses into a unified report.
2
+
3
+ ## Structure Analysis
4
+ Architecture: {{ structure.architecture.organization }}
5
+ Patterns: {% for p in structure.architecture.patterns %}{{ p }}{% if not loop.last %}, {% endif %}{% endfor %}
6
+
7
+ ### Modules
8
+ {% for m in structure.modules %}
9
+ - **{{ m.name }}**: {{ m.responsibility }} ({{ m.files | length }} files)
10
+ {% endfor %}
11
+
12
+ ### Dependencies
13
+ {% for d in structure.dependencies %}
14
+ - `{{ d.from }}` → `{{ d.to }}` ({{ d.type }})
15
+ {% endfor %}
16
+
17
+ ## Quality Analysis
18
+ {% for c in quality.concerns %}
19
+ - [{{ c.severity }}] {{ c.description }}{% if c.file %} in `{{ c.file }}`{% endif %}
20
+ {% endfor %}
21
+
22
+ ### Test Coverage
23
+ - Tested: {% for t in quality.testCoverage.tested %}`{{ t }}`{% if not loop.last %}, {% endif %}{% endfor %}
24
+ - Untested: {% for t in quality.testCoverage.untested %}`{{ t }}`{% if not loop.last %}, {% endif %}{% endfor %}
25
+
26
+ ### Maintainability: {{ quality.maintainability.score }}
27
+
28
+ ## Pattern Analysis
29
+ {% for p in patterns.patterns %}
30
+ - **{{ p.name }}**: {{ p.usage }}{% if not p.correct %} ⚠️{% endif %}
31
+ {% endfor %}
32
+
33
+ ### Recommendations
34
+ {% for r in patterns.recommendations %}
35
+ - [{{ r.priority }}] **{{ r.pattern }}**: {{ r.suggestion }}
36
+ {% endfor %}
37
+
38
+ Write your synthesis as JSON conforming to the output schema.
@@ -0,0 +1,57 @@
1
+ You are verifying the output of a prior step against declared success criteria.
2
+
3
+ ## Success Criteria
4
+
5
+ {% for criterion in criteria %}
6
+ - {{ criterion }}
7
+ {% endfor %}
8
+
9
+ ## Step Output to Verify
10
+
11
+ ```json
12
+ {{ step_output | dump(2) }}
13
+ ```
14
+
15
+ {% if files_to_check %}
16
+ ## Files to Check
17
+
18
+ {% for f in files_to_check %}
19
+ - `{{ f }}`
20
+ {% endfor %}
21
+ {% endif %}
22
+
23
+ ## Instructions
24
+
25
+ For each criterion, choose the appropriate verification method and execute it:
26
+
27
+ 1. **command** — Run a shell command (test suite, build, lint) and report exit code + output as evidence
28
+ 2. **grep** — Search for specific patterns in files and report matches as evidence
29
+ 3. **inspect** — Read files and assess their content (use your judgment)
30
+ 4. **human** — Flag items that require human eyes and explain why automation cannot verify them
31
+
32
+ ### Process
33
+
34
+ 1. Evaluate each success criterion using the most appropriate verify method
35
+ 2. For `command` criteria: actually run the command and capture the output
36
+ 3. For `grep` criteria: search the relevant files and report what you find
37
+ 4. For `inspect` criteria: read the files and assess whether they meet the criterion
38
+ 5. For `human` criteria: describe what needs to be checked and why it needs a person
39
+ 6. Build truth claims from your observations, marking each as `verified`, `failed`, or `uncertain`
40
+ 7. Check that all referenced artifacts exist and are substantive (not stubs)
41
+ 8. Compute an overall score as "N/M" where N = passed criteria, M = total criteria
42
+ 9. Set status: `passed` if all criteria pass, `gaps_found` if any fail, `human_needed` if any require human review
43
+
44
+ ### Output Format
45
+
46
+ Produce JSON conforming to the verifier-output schema with these fields:
47
+
48
+ - `status`: "passed" | "gaps_found" | "human_needed"
49
+ - `score`: "N/M" format
50
+ - `truths[]`: observable claims with status and evidence
51
+ - `criteria_results[]`: one entry per criterion with verify_method, status, expected/actual outcome, evidence
52
+ - `artifacts[]`: (optional) file paths checked with existence/substance status
53
+ - `requirements_coverage[]`: (optional) requirement satisfaction mapping
54
+ - `human_verification[]`: (optional) items needing human review
55
+ - `gaps[]`: (optional) failed or uncertain truths with reasons
56
+
57
+ Be thorough. Run actual commands. Read actual files. Report what you observe, not what you assume.
@@ -0,0 +1,67 @@
1
+ name: create-phase
2
+ description: Convert unstructured intent into a structured phase spec
3
+ version: "1"
4
+
5
+ input:
6
+ type: object
7
+ required: [intent]
8
+ properties:
9
+ intent:
10
+ type: string
11
+ description: Unstructured intent from conversation — what needs to be built and why
12
+
13
+ steps:
14
+ load-context:
15
+ command: |
16
+ node --experimental-strip-types -e "
17
+ import fs from 'fs';
18
+ import { readBlock } from './src/block-api.ts';
19
+ import { PROJECT_DIR } from './src/project-dir.ts';
20
+ import path from 'path';
21
+ const phasesDir = path.join(PROJECT_DIR, 'phases');
22
+ const phases = fs.readdirSync(phasesDir)
23
+ .filter(f => f.endsWith('.json'))
24
+ .map(f => JSON.parse(fs.readFileSync(path.join(phasesDir, f), 'utf8')));
25
+ const arch = readBlock('.', 'architecture');
26
+ const conv = readBlock('.', 'conventions');
27
+ const gapsData = readBlock('.', 'gaps');
28
+ const inv = readBlock('.', 'inventory');
29
+ console.log(JSON.stringify({ phases, architecture: arch, conventions: conv, gaps: gapsData.gaps, inventory: inv }));
30
+ "
31
+ output:
32
+ format: json
33
+
34
+ author:
35
+ agent: phase-author
36
+ input:
37
+ intent: "${{ input.intent }}"
38
+ phases: "${{ steps.load-context.output.phases }}"
39
+ architecture: "${{ steps.load-context.output.architecture }}"
40
+ conventions: "${{ steps.load-context.output.conventions }}"
41
+ gaps: "${{ steps.load-context.output.gaps }}"
42
+ inventory: "${{ steps.load-context.output.inventory }}"
43
+ output:
44
+ format: json
45
+ schema: ../../.project/schemas/phase.schema.json
46
+
47
+ write-phase:
48
+ command: |
49
+ node --experimental-strip-types -e "
50
+ import fs from 'fs';
51
+ import { validateFromFile } from './src/schema-validator.ts';
52
+ const phase = JSON.parse(process.argv[1]);
53
+ validateFromFile('.project/schemas/phase.schema.json', phase, 'phase');
54
+ const filename = '.project/phases/' + String(phase.number).padStart(2, '0') + '-' + phase.name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/-+$/, '') + '.json';
55
+ fs.writeFileSync(filename, JSON.stringify(phase, null, 2) + '\n');
56
+ console.log(JSON.stringify({ path: filename, phase_number: phase.number, spec_count: phase.specs.length }));
57
+ " '${{ steps.author.output | json }}'
58
+ output:
59
+ format: json
60
+
61
+ completion:
62
+ message: |
63
+ Phase ${{ steps.write-phase.output.phase_number }} created at ${{ steps.write-phase.output.path }}
64
+ Contains ${{ steps.write-phase.output.spec_count }} specs.
65
+ include:
66
+ - steps.author.output.success_criteria
67
+ - steps.author.output.specs
@@ -0,0 +1,153 @@
1
+ name: do-gap
2
+ description: Standard path from gap to implementation — investigate, research, decompose, implement, verify, route
3
+ version: "1"
4
+
5
+ input:
6
+ type: object
7
+ required: [gap_id]
8
+ properties:
9
+ gap_id:
10
+ type: string
11
+ description: ID of the gap to work on
12
+ source:
13
+ file: .project/gaps.json
14
+ array: gaps
15
+ filter: { status: open }
16
+ label: id
17
+ value: id
18
+
19
+ steps:
20
+ load:
21
+ command: |
22
+ node --experimental-strip-types -e "
23
+ import { readBlock } from './src/block-api.ts';
24
+ const gapId = process.argv[1];
25
+ const data = readBlock('.', 'gaps');
26
+ const gap = data.gaps.find(g => g.id === gapId);
27
+ if (!gap) { console.error('Gap not found: ' + gapId); process.exit(1); }
28
+ if (gap.status === 'resolved') { console.error('Gap already resolved: ' + gapId); process.exit(1); }
29
+ console.log(JSON.stringify({ gap }));
30
+ " '${{ input.gap_id }}'
31
+ output:
32
+ format: json
33
+
34
+ investigate:
35
+ agent: investigator
36
+ input:
37
+ gap: "${{ steps.load.output.gap }}"
38
+ output:
39
+ format: json
40
+ schema: schemas/investigation-findings.schema.json
41
+
42
+ research:
43
+ when: "${{ steps.investigate.output.needs_research }}"
44
+ agent: researcher
45
+ input:
46
+ gap: "${{ steps.load.output.gap }}"
47
+ research_questions: "${{ steps.investigate.output.research_questions }}"
48
+ output:
49
+ format: json
50
+ schema: schemas/research-findings.schema.json
51
+
52
+ decompose:
53
+ agent: decomposer
54
+ input:
55
+ gap: "${{ steps.load.output.gap }}"
56
+ investigation: "${{ steps.investigate.output }}"
57
+ research: "${{ steps.research.output }}"
58
+ output:
59
+ format: json
60
+ schema: schemas/decomposition-specs.schema.json
61
+
62
+ implement:
63
+ forEach: "${{ steps.decompose.output.specs }}"
64
+ as: spec
65
+ agent: spec-implementer
66
+ input:
67
+ spec: "${{ spec }}"
68
+ gap: "${{ steps.load.output.gap }}"
69
+ investigation: "${{ steps.investigate.output }}"
70
+ retry:
71
+ maxAttempts: 2
72
+ onExhausted: fail
73
+ output:
74
+ format: json
75
+ schema: schemas/execution-results.schema.json
76
+
77
+ verify:
78
+ command: |
79
+ node --experimental-strip-types -e "
80
+ import { execSync } from 'child_process';
81
+ const errors = [];
82
+
83
+ // Run tests
84
+ try {
85
+ execSync('node --experimental-strip-types --test src/*.test.ts', { stdio: 'pipe', timeout: 120000 });
86
+ } catch (e) {
87
+ errors.push('Tests failed: ' + (e.stderr?.toString().slice(-500) || 'unknown error'));
88
+ }
89
+
90
+ // Run linter on changed files
91
+ try {
92
+ const diff = execSync('git diff --name-only HEAD', { encoding: 'utf8' }).trim();
93
+ const srcFiles = diff.split('\n').filter(f => f.startsWith('src/') && f.endsWith('.ts') && !f.endsWith('.test.ts'));
94
+ for (const f of srcFiles) {
95
+ try {
96
+ execSync('/Users/david/Projects/pi-extension-linter/pi-extension-lint.sh ' + f, { stdio: 'pipe' });
97
+ } catch (e) {
98
+ const stderr = e.stderr?.toString() || e.stdout?.toString() || '';
99
+ if (stderr.includes('error')) errors.push('Lint errors in ' + f);
100
+ }
101
+ }
102
+ } catch {}
103
+
104
+ // Validate blocks
105
+ try {
106
+ const fs = await import('fs');
107
+ const schemasDir = '.project/schemas';
108
+ const schemas = fs.readdirSync(schemasDir).filter(f => f.endsWith('.schema.json'));
109
+ for (const s of schemas) {
110
+ const blockName = s.replace('.schema.json', '');
111
+ const blockPath = '.project/' + blockName + '.json';
112
+ if (fs.existsSync(blockPath)) {
113
+ try {
114
+ execSync('node --experimental-strip-types -e \"import{validateFromFile}from' + String.fromCharCode(39) + './src/schema-validator.ts' + String.fromCharCode(39) + ';import fs from' + String.fromCharCode(39) + 'fs' + String.fromCharCode(39) + ';validateFromFile(' + String.fromCharCode(39) + schemasDir + '/' + s + String.fromCharCode(39) + ',JSON.parse(fs.readFileSync(' + String.fromCharCode(39) + blockPath + String.fromCharCode(39) + ',' + String.fromCharCode(39) + 'utf8' + String.fromCharCode(39) + ')),' + String.fromCharCode(39) + blockName + String.fromCharCode(39) + ')\"', { stdio: 'pipe' });
115
+ } catch { errors.push('Block validation failed: ' + blockPath); }
116
+ }
117
+ }
118
+ } catch {}
119
+
120
+ const passed = errors.length === 0;
121
+ console.log(JSON.stringify({ status: passed ? 'passed' : 'failed', errors, passed }));
122
+ "
123
+ output:
124
+ format: json
125
+
126
+ route:
127
+ when: "${{ steps.verify.output.passed }}"
128
+ command: |
129
+ node --experimental-strip-types -e "
130
+ import { updateItemInBlock } from './src/block-api.ts';
131
+ const gapId = process.argv[1];
132
+ updateItemInBlock('.', 'gaps', 'gaps',
133
+ (g) => g.id === gapId,
134
+ { status: 'resolved', resolved_by: 'do-gap-workflow' }
135
+ );
136
+ console.log(JSON.stringify({ routed: true, gap_id: gapId, status: 'resolved' }));
137
+ " '${{ input.gap_id }}'
138
+ output:
139
+ format: json
140
+
141
+ check:
142
+ when: "${{ steps.route.output.routed }}"
143
+ gate:
144
+ check: "test '${{ steps.verify.output.status }}' = 'passed'"
145
+ onFail: fail
146
+
147
+ completion:
148
+ message: |
149
+ Gap ${{ input.gap_id }} — ${{ steps.verify.output.status }}
150
+ Investigation complexity: ${{ steps.investigate.output.complexity }}
151
+ Specs implemented: ${{ steps.decompose.output.specs | length }}
152
+ include:
153
+ - steps.verify.output