@surlymochan/evalid 0.0.0-stage → 0.3.1
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.
- package/CHANGELOG.md +74 -0
- package/LICENSE +21 -0
- package/README.md +315 -2
- package/RELEASE_NOTES.md +102 -0
- package/atoms/registry.json +758 -0
- package/bin/xflow.js +7208 -0
- package/docs/adoption/README.md +128 -0
- package/docs/adoption/openflow-release-hardening.md +61 -0
- package/docs/benchmark-openflow-vs-gstack-superpowers-openspec-2026-05-25.md +81 -0
- package/docs/compatibility.md +52 -0
- package/docs/competitive-benchmark.md +108 -0
- package/docs/corps-operator-guide.md +75 -0
- package/docs/demo-proof.md +126 -0
- package/docs/examples-gallery.md +115 -0
- package/docs/fixtures/tracker-item.json +7 -0
- package/docs/goal-vs-codex.md +129 -0
- package/docs/gstack-comparison.md +88 -0
- package/docs/install-upgrade.md +133 -0
- package/docs/integrations.md +49 -0
- package/docs/launch-demo.md +123 -0
- package/docs/launch-dossier.md +180 -0
- package/docs/methodology.md +149 -0
- package/docs/npm-publish-handoff.md +83 -0
- package/docs/openspec-migration.md +106 -0
- package/docs/public-benchmark.md +68 -0
- package/docs/public-release.md +145 -0
- package/docs/quality-assessment.md +139 -0
- package/docs/quickstart.md +156 -0
- package/docs/reviewer-guide.md +43 -0
- package/docs/spec-kit-benchmark.md +73 -0
- package/docs/super-assistant-comparison.md +106 -0
- package/docs/superpowers-comparison.md +82 -0
- package/docs/team-adoption.md +123 -0
- package/docs/tooling-matrix.md +91 -0
- package/docs/walkthrough.md +76 -0
- package/docs/workflow-manual.md +1125 -0
- package/package.json +83 -4
- package/prompts/pencil-draft.md +46 -0
- package/prompts/pencil-refine.md +46 -0
- package/prompts/phase-artifact.md +24 -0
- package/prompts/phase-execution.md +32 -0
- package/schemas/competitor-reconstruction-contract.schema.json +91 -0
- package/schemas/llm-design-review-aggregate.schema.json +83 -0
- package/schemas/visual-benchmark.schema.json +978 -0
- package/schemas/workflow.schema.json +100 -0
- package/scripts/run-tests.mjs +23 -0
- package/scripts/takein_digest.py +139 -0
- package/specs/README.md +118 -0
- package/specs/architecture.md +36 -0
- package/specs/product.md +33 -0
- package/specs/workflow.md +349 -0
- package/src/agent_team/atoms/codex-app-bridge.js +8 -0
- package/src/agent_team/atoms/design/competitor-reconstruction-review.js +44 -0
- package/src/agent_team/atoms/design/contract-freeze.js +8 -0
- package/src/agent_team/atoms/design/generation-contract.js +442 -0
- package/src/agent_team/atoms/design/reconstruction-pack.js +246 -0
- package/src/agent_team/atoms/design/reference-surface-lock.js +49 -0
- package/src/agent_team/atoms/patch-challenge.js +19 -0
- package/src/agent_team/atoms/pencil/accept.js +9 -0
- package/src/agent_team/atoms/pencil/draft.js +365 -0
- package/src/agent_team/atoms/pencil/refine.js +229 -0
- package/src/agent_team/atoms/phase-run-dispatch.js +8 -0
- package/src/agent_team/atoms/team-run.js +380 -0
- package/src/agent_team/atoms/visual/aesthetic-review.js +231 -0
- package/src/agent_team/atoms/visual/benchmark-repair-plan.js +291 -0
- package/src/agent_team/atoms/visual/benchmark.js +1652 -0
- package/src/agent_team/atoms/visual/design-selection.js +23 -0
- package/src/agent_team/atoms/visual/direction-synthesis.js +124 -0
- package/src/agent_team/atoms/visual/dom-rect-export.js +129 -0
- package/src/agent_team/atoms/visual/image-diff.js +186 -0
- package/src/agent_team/atoms/visual/layout-competition.js +8 -0
- package/src/agent_team/atoms/visual/page-evidence-capture.js +806 -0
- package/src/agent_team/atoms/visual/playwright-runtime.js +8 -0
- package/src/agent_team/atoms/visual/review-aggregate.js +87 -0
- package/src/agent_team/atoms/visual/review.js +77 -0
- package/src/agent_team/atoms/visual/visual-report.js +197 -0
- package/src/agent_team/atoms/visual/visual-token-export.js +417 -0
- package/src/agent_team/common.js +311 -0
- package/src/core/atoms/agent-invoke.js +684 -0
- package/src/core/atoms/event/ingest.js +30 -0
- package/src/core/atoms/experience/digest.js +55 -0
- package/src/core/atoms/gate/artifact-verify.js +83 -0
- package/src/core/atoms/gate/competitor-contract-validate.js +119 -0
- package/src/core/atoms/gate/evidence-collect.js +64 -0
- package/src/core/atoms/gate/payload-from-phase-run.js +78 -0
- package/src/core/atoms/gate/visual-benchmark-validate.js +183 -0
- package/src/core/atoms/gate/workbench.js +79 -0
- package/src/core/atoms/intake/classify.js +15 -0
- package/src/core/atoms/intake/design-skill-select.js +16 -0
- package/src/core/atoms/intake/preview-or-refine.js +19 -0
- package/src/core/atoms/intake/recommend.js +20 -0
- package/src/core/atoms/intake/research-same-category.js +19 -0
- package/src/core/atoms/intervention/create.js +30 -0
- package/src/core/atoms/memory/manage.js +27 -0
- package/src/core/atoms/memory/mount.js +13 -0
- package/src/core/atoms/mission/create.js +19 -0
- package/src/core/atoms/mission/list.js +14 -0
- package/src/core/atoms/mission/show.js +26 -0
- package/src/core/atoms/notify/checkpoint.js +46 -0
- package/src/core/atoms/phase-run/complete.js +42 -0
- package/src/core/atoms/phase-run/fail.js +40 -0
- package/src/core/atoms/phase-run/persist.js +39 -0
- package/src/core/atoms/phase-run/start.js +30 -0
- package/src/core/atoms/plugin/run.js +76 -0
- package/src/core/atoms/policy/gate.js +90 -0
- package/src/core/atoms/policy/resolve.js +11 -0
- package/src/core/atoms/storage/sync.js +57 -0
- package/src/core/atoms/workflow/advance.js +25 -0
- package/src/core/atoms/workflow/guard.js +127 -0
- package/src/core/atoms/workflow/state-load.js +37 -0
- package/src/core/bindings.js +37 -0
- package/src/core/change-artifacts.js +134 -0
- package/src/core/competitive-score.js +265 -0
- package/src/core/concurrency-policy.js +35 -0
- package/src/core/execution-log.js +108 -0
- package/src/core/intake-recommendation.js +134 -0
- package/src/core/memory-state.js +62 -0
- package/src/core/mission-state.js +780 -0
- package/src/core/pending-gates.js +66 -0
- package/src/core/phase-cost.js +116 -0
- package/src/core/phase-run-read.js +27 -0
- package/src/core/policy-overlay.js +180 -0
- package/src/core/state-store.js +201 -0
- package/src/core/workflow-executor.js +998 -0
- package/src/core/workflow-integrity.js +111 -0
- package/src/core/workflow-loader.js +163 -0
- package/src/core/workflow-rerun.js +84 -0
- package/src/core/workflow-state.js +119 -0
- package/src/server.js +1307 -0
- package/workflows/corps.yaml +479 -0
- package/workflows/yolo.yaml +233 -0
- package/xflow/README.md +76 -0
- package/xflow/aha/SKILL.md +82 -0
- package/xflow/aha/scripts/append_aha.py +50 -0
- package/xflow/archive/scripts/archive_change.py +338 -0
- package/xflow/archive/templates/merge-design.md +54 -0
- package/xflow/archive/templates/merge-workflow.md +26 -0
- package/xflow/atoms/__init__.py +6 -0
- package/xflow/atoms/a1_issue_create_or_patch.py +70 -0
- package/xflow/atoms/a2_branch_create_and_link.py +42 -0
- package/xflow/atoms/a3_repo_doctor.py +11 -0
- package/xflow/atoms/a4_project_set_status.py +11 -0
- package/xflow/atoms/a5_archive_commit_push_close.py +11 -0
- package/xflow/atoms/a6_pr_create.py +72 -0
- package/xflow/atoms/b1_change_scaffold.py +11 -0
- package/xflow/atoms/b2_status_read.py +16 -0
- package/xflow/atoms/b2b_status_write.py +20 -0
- package/xflow/atoms/b3_status_transition.py +27 -0
- package/xflow/atoms/b4_handoff_scaffold_or_refresh.py +11 -0
- package/xflow/atoms/b5_aha_append.py +11 -0
- package/xflow/atoms/b6_takein_digest.py +11 -0
- package/xflow/atoms/b6b_lookback_digest.py +11 -0
- package/xflow/atoms/b7_change_mission_bind.py +43 -0
- package/xflow/atoms/c1_mem.py +54 -0
- package/xflow/atoms/e6_gate_local_precheck.py +23 -0
- package/xflow/atoms/h1_design_lite_gate.py +74 -0
- package/xflow/atoms/i6a_tdd_run.py +104 -0
- package/xflow/atoms/i6b_tdd_proof_validate.py +165 -0
- package/xflow/atoms/i6c_tdd_quality_review.py +357 -0
- package/xflow/atoms/j1_tests_run.py +129 -0
- package/xflow/atoms/j4a_spec_consistency_structural.py +105 -0
- package/xflow/atoms/j4b_spec_delta_review.py +316 -0
- package/xflow/atoms/j4c_openspec_migration_map.py +121 -0
- package/xflow/atoms/j4d_spec_clarify_gate.py +373 -0
- package/xflow/atoms/j6_cr_review.py +443 -0
- package/xflow/atoms/k1_artifacts_complete_check.py +39 -0
- package/xflow/atoms/k2_merge_snippets_apply.py +11 -0
- package/xflow/atoms/k3_handoff_refresh.py +14 -0
- package/xflow/atoms/k4_mem_lesson_persist.py +65 -0
- package/xflow/atoms/k5_archive_publish.py +21 -0
- package/xflow/atoms/k6_aha_merge.py +42 -0
- package/xflow/atoms/tdd_lib.py +747 -0
- package/xflow/corps/SKILL.md +109 -0
- package/xflow/exec/scripts/set_issue_in_progress.py +159 -0
- package/xflow/goal/SKILL.md +150 -0
- package/xflow/handoff/SKILL.md +95 -0
- package/xflow/handoff/scripts/scaffold_handoff.py +304 -0
- package/xflow/lookback/SKILL.md +66 -0
- package/xflow/lookback/scripts/lookback_digest.py +46 -0
- package/xflow/openissue/scripts/open_issue_flow.py +438 -0
- package/xflow/plan/SKILL.md +38 -0
- package/xflow/ralph/SKILL.md +123 -0
- package/xflow/scripts/check_installed_xflow_skill_sync.sh +44 -0
- package/xflow/scripts/common.py +480 -0
- package/xflow/scripts/doctor.py +278 -0
- package/xflow/scripts/sync_installed_xflow_skill.sh +134 -0
- package/xflow/takein/SKILL.md +81 -0
- package/xflow/takein/scripts/takein_digest.py +88 -0
- package/xflow/yolo/SKILL.md +66 -0
- package/xflow/yolo/scripts/scaffold_specs_change.py +673 -0
- package/xflow/yolo/scripts/yolo_gate.py +312 -0
- package/xflow/yolo/templates/DESIGN.md +179 -0
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'fs';
|
|
2
|
+
import { dirname, resolve } from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
|
|
5
|
+
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
6
|
+
|
|
7
|
+
const STATUS_POINTS = {
|
|
8
|
+
strong: 10,
|
|
9
|
+
improving: 8,
|
|
10
|
+
needs_work: 5,
|
|
11
|
+
weak: 2,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const DIMENSIONS = [
|
|
15
|
+
{
|
|
16
|
+
id: 'local_delivery_closure',
|
|
17
|
+
label: 'Local delivery closure',
|
|
18
|
+
evidence: [
|
|
19
|
+
['file', 'workflows/yolo.yaml'],
|
|
20
|
+
['file', 'workflows/corps.yaml'],
|
|
21
|
+
['text', 'HANDOFF.md', /archive/i],
|
|
22
|
+
],
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: 'executable_workflow_data',
|
|
26
|
+
label: 'Executable workflow data',
|
|
27
|
+
evidence: [
|
|
28
|
+
['file', 'schemas/workflow.schema.json'],
|
|
29
|
+
['text', 'test/workflow-loader.test.js', /Workflow: yolo\.yaml/],
|
|
30
|
+
['text', 'test/workflow-loader.test.js', /Workflow: corps\.yaml/],
|
|
31
|
+
],
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
id: 'goal_alignment',
|
|
35
|
+
label: 'Goal alignment',
|
|
36
|
+
evidence: [
|
|
37
|
+
['file', 'docs/goal-vs-codex.md'],
|
|
38
|
+
['text', 'bin/xflow.js', /case 'goal'/],
|
|
39
|
+
['text', 'bin/xflow.js', /goal audit/],
|
|
40
|
+
['text', 'bin/xflow.js', /codex-goal/],
|
|
41
|
+
['text', 'README.md', /xflow goal set/],
|
|
42
|
+
['text', 'README.md', /docs\/goal-vs-codex\.md/],
|
|
43
|
+
['text', 'docs/tooling-matrix.md', /skill family as a ladder/],
|
|
44
|
+
['text', 'docs/tooling-matrix.md', /xflow goal set/],
|
|
45
|
+
['text', 'docs/competitive-benchmark.md', /Goal alignment/],
|
|
46
|
+
['text', 'docs/competitive-benchmark.md', /xflow compare codex-goal/],
|
|
47
|
+
['text', 'docs/competitive-benchmark.md', /xflow goal show --json/],
|
|
48
|
+
['text', 'docs/competitive-benchmark.md', /xflow goal audit --json/],
|
|
49
|
+
['text', 'xflow/goal/SKILL.md', /Advantage Over Codex Native Goal/],
|
|
50
|
+
['text', 'xflow/yolo/SKILL.md', /aligns with it, intentionally narrows it, or conflicts with it/],
|
|
51
|
+
['text', 'xflow/corps/SKILL.md', /final proof review/],
|
|
52
|
+
['text', 'xflow/ralph/SKILL.md', /intentionally diverges from the goal/],
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
id: 'verification_rigor',
|
|
57
|
+
label: 'Verification rigor',
|
|
58
|
+
evidence: [
|
|
59
|
+
['text', 'package.json', /"drift:scan"/],
|
|
60
|
+
['text', 'package.json', /"skill:diff"/],
|
|
61
|
+
['file', 'test/release-checklist.test.js'],
|
|
62
|
+
],
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
id: 'tdd_code_quality_push',
|
|
66
|
+
label: 'TDD / code quality push',
|
|
67
|
+
evidence: [
|
|
68
|
+
['text', 'xflow/atoms/i6a_tdd_run.py', /Red expects a failing test/],
|
|
69
|
+
['text', 'xflow/atoms/i6b_tdd_proof_validate.py', /Red must prove a failing test/],
|
|
70
|
+
['text', 'xflow/atoms/i6c_tdd_quality_review.py', /code changes should be accompanied by test changes/],
|
|
71
|
+
['file', 'test/tdd-proof.test.js'],
|
|
72
|
+
['text', 'workflows/yolo.yaml', /tdd-red[\s\S]*execute[\s\S]*tdd-green/],
|
|
73
|
+
['text', 'workflows/corps.yaml', /I6c\.tdd\.quality_review/],
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
id: 'operator_onboarding',
|
|
78
|
+
label: 'Operator onboarding',
|
|
79
|
+
evidence: [
|
|
80
|
+
['text', 'README.md', /3 Minute Start/],
|
|
81
|
+
['text', 'bin/xflow.js', /case 'init'/],
|
|
82
|
+
['text', 'bin/xflow.js', /case 'doctor'/],
|
|
83
|
+
['text', 'bin/xflow.js', /case 'guide'/],
|
|
84
|
+
['text', 'docs/quickstart.md', /xflow init --project-root \./],
|
|
85
|
+
['file', 'docs/walkthrough.md'],
|
|
86
|
+
],
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
id: 'spec_durability',
|
|
90
|
+
label: 'Spec durability',
|
|
91
|
+
evidence: [
|
|
92
|
+
['file', 'specs/workflow.md'],
|
|
93
|
+
['file', 'AHA.md'],
|
|
94
|
+
['file', 'HANDOFF.md'],
|
|
95
|
+
['file', 'docs/openspec-migration.md'],
|
|
96
|
+
['text', 'xflow/atoms/j4b_spec_delta_review.py', /Spec Delta Review/],
|
|
97
|
+
['text', 'xflow/atoms/j4c_openspec_migration_map.py', /OpenSpec project to evalid surfaces/],
|
|
98
|
+
],
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
id: 'cross_tool_portability',
|
|
102
|
+
label: 'Cross-tool portability',
|
|
103
|
+
evidence: [
|
|
104
|
+
['file', 'docs/tooling-matrix.md'],
|
|
105
|
+
['file', 'docs/integrations.md'],
|
|
106
|
+
['text', 'docs/tooling-matrix.md', /Codex CLI \/ App/],
|
|
107
|
+
['text', 'docs/tooling-matrix.md', /Gemini CLI/],
|
|
108
|
+
['text', 'docs/compatibility.md', /Claude Code/],
|
|
109
|
+
['text', 'docs/compatibility.md', /Cursor/],
|
|
110
|
+
['text', 'docs/compatibility.md', /OpenCode/],
|
|
111
|
+
['text', 'bin/xflow.js', /case 'adapter'/],
|
|
112
|
+
['text', 'docs/integrations.md', /xflow adapter import-file/],
|
|
113
|
+
['text', 'xflow/openissue/scripts/open_issue_flow.py', /configured_issue_repo/],
|
|
114
|
+
['text', 'xflow/scripts/sync_installed_xflow_skill.sh', /config_value/],
|
|
115
|
+
],
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
id: 'public_product_clarity',
|
|
119
|
+
label: 'Public product clarity',
|
|
120
|
+
evidence: [
|
|
121
|
+
['file', 'README.md'],
|
|
122
|
+
['file', 'docs/competitive-benchmark.md'],
|
|
123
|
+
['file', 'docs/superpowers-comparison.md'],
|
|
124
|
+
['file', 'docs/quality-assessment.md'],
|
|
125
|
+
['file', 'docs/launch-demo.md'],
|
|
126
|
+
['file', 'docs/launch-dossier.md'],
|
|
127
|
+
['file', 'docs/examples-gallery.md'],
|
|
128
|
+
['file', 'docs/corps-operator-guide.md'],
|
|
129
|
+
['file', 'docs/adoption/README.md'],
|
|
130
|
+
['file', 'docs/adoption/openflow-release-hardening.md'],
|
|
131
|
+
['file', 'docs/demo-proof.md'],
|
|
132
|
+
['file', 'docs/openspec-migration.md'],
|
|
133
|
+
['file', 'docs/spec-kit-benchmark.md'],
|
|
134
|
+
['file', 'docs/public-benchmark.md'],
|
|
135
|
+
['file', 'docs/npm-publish-handoff.md'],
|
|
136
|
+
['file', 'docs/walkthrough.md'],
|
|
137
|
+
['file', 'docs/methodology.md'],
|
|
138
|
+
['file', 'RELEASE_NOTES.md'],
|
|
139
|
+
['file', 'docs/quickstart.md'],
|
|
140
|
+
['text', 'xflow/README.md', /docs\/quickstart\.md/],
|
|
141
|
+
['text', 'bin/xflow.js', /case 'assess'/],
|
|
142
|
+
['text', 'bin/xflow.js', /case 'demo'/],
|
|
143
|
+
['text', 'bin/xflow.js', /case 'launch'/],
|
|
144
|
+
['text', 'bin/xflow.js', /case 'quickstart'/],
|
|
145
|
+
['text', 'bin/xflow.js', /launch dossier/],
|
|
146
|
+
['text', 'bin/xflow.js', /case 'adoption'/],
|
|
147
|
+
['text', 'bin/xflow.js', /case 'package'/],
|
|
148
|
+
['text', 'docs/quality-assessment.md', /xflow assess --json/],
|
|
149
|
+
['text', 'docs/launch-demo.md', /goal -> yolo/],
|
|
150
|
+
['text', 'docs/launch-demo.md', /goal -> corps proof/],
|
|
151
|
+
['text', 'docs/examples-gallery.md', /Goal To yolo/],
|
|
152
|
+
['text', 'docs/examples-gallery.md', /External Tracker Import/],
|
|
153
|
+
['text', 'docs/launch-dossier.md', /Missing Before Splash Launch/],
|
|
154
|
+
['text', 'docs/launch-dossier.md', /published_package/],
|
|
155
|
+
['text', 'docs/corps-operator-guide.md', /xflow corps --explain/],
|
|
156
|
+
['text', 'docs/launch-demo.md', /xflow launch dossier/],
|
|
157
|
+
['text', 'docs/public-release.md', /xflow launch audit --strict --json/],
|
|
158
|
+
['text', 'docs/public-release.md', /xflow launch audit --pre-publish --strict --json/],
|
|
159
|
+
['text', 'docs/public-release.md', /xflow adoption validate --json/],
|
|
160
|
+
['text', 'docs/public-release.md', /xflow package preflight --check-registry --check-auth --json/],
|
|
161
|
+
['text', 'docs/public-release.md', /xflow package audit --check-registry --json/],
|
|
162
|
+
['text', 'docs/npm-publish-handoff.md', /npm publish --access public/],
|
|
163
|
+
['text', 'docs/adoption/README.md', /Required Shape/],
|
|
164
|
+
['text', 'docs/adoption/README.md', /Acceptance Bar/],
|
|
165
|
+
['text', 'docs/adoption/README.md', /xflow adoption init/],
|
|
166
|
+
['text', 'docs/adoption/README.md', /xflow adoption validate --json/],
|
|
167
|
+
['text', 'docs/adoption/openflow-release-hardening.md', /xflow launch audit --strict --json/],
|
|
168
|
+
['text', 'bin/xflow.js', /case 'spec'/],
|
|
169
|
+
['text', 'bin/xflow.js', /case 'adapter'/],
|
|
170
|
+
['text', 'bin/xflow.js', /case 'compare'/],
|
|
171
|
+
['text', 'bin/xflow.js', /superpowers/],
|
|
172
|
+
['text', 'bin/xflow.js', /case 'score'/],
|
|
173
|
+
],
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
id: 'team_collaboration',
|
|
177
|
+
label: 'Team collaboration',
|
|
178
|
+
evidence: [
|
|
179
|
+
['file', 'docs/reviewer-guide.md'],
|
|
180
|
+
['text', 'docs/reviewer-guide.md', /verify_proof\.json/],
|
|
181
|
+
['text', 'docs/reviewer-guide.md', /A5\.archive\.commit_push_close/],
|
|
182
|
+
['file', 'docs/team-adoption.md'],
|
|
183
|
+
['text', 'docs/team-adoption.md', /95 Percent Readiness Gate/],
|
|
184
|
+
['text', 'docs/team-adoption.md', /Operator/],
|
|
185
|
+
['text', 'docs/team-adoption.md', /Release owner/],
|
|
186
|
+
['text', 'docs/team-adoption.md', /PR Acceptance Checklist/],
|
|
187
|
+
['text', 'README.md', /docs\/team-adoption\.md/],
|
|
188
|
+
],
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
id: 'control_plane_observability',
|
|
192
|
+
label: 'Control-plane observability',
|
|
193
|
+
evidence: [
|
|
194
|
+
['file', 'src/server.js'],
|
|
195
|
+
['text', 'test/server.test.js', /timeline analytics/],
|
|
196
|
+
['text', 'test/server.test.js', /orphaned/],
|
|
197
|
+
],
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
id: 'ecosystem_packaging',
|
|
201
|
+
label: 'Ecosystem packaging',
|
|
202
|
+
evidence: [
|
|
203
|
+
['text', 'package.json', /"bin"/],
|
|
204
|
+
['text', 'package.json', /"pack:check"/],
|
|
205
|
+
['text', 'package.json', /"release:pack"/],
|
|
206
|
+
['text', 'package.json', /"publish:check"/],
|
|
207
|
+
['text', 'package.json', /"README\.md"/],
|
|
208
|
+
['text', 'package.json', /"RELEASE_NOTES\.md"/],
|
|
209
|
+
['file', 'docs/install-upgrade.md'],
|
|
210
|
+
['file', 'docs/public-release.md'],
|
|
211
|
+
['file', 'docs/public-benchmark.md'],
|
|
212
|
+
['file', 'docs/npm-publish-handoff.md'],
|
|
213
|
+
['text', 'docs/install-upgrade.md', /npm install -g @surlymochan\/evalid/],
|
|
214
|
+
['text', 'docs/install-upgrade.md', /xflow package preflight --check-registry --check-auth --json/],
|
|
215
|
+
['file', 'docs/demo-proof.md'],
|
|
216
|
+
['file', 'docs/fixtures/tracker-item.json'],
|
|
217
|
+
['text', '.github/workflows/ci.yml', /Minimal External Adoption Example/],
|
|
218
|
+
],
|
|
219
|
+
},
|
|
220
|
+
];
|
|
221
|
+
|
|
222
|
+
export function buildCompetitiveScore({ root = ROOT } = {}) {
|
|
223
|
+
const dimensions = DIMENSIONS.map((dimension) => {
|
|
224
|
+
const evidence = dimension.evidence.map((item) => evaluateEvidence(root, item));
|
|
225
|
+
const passed = evidence.filter((item) => item.ok).length;
|
|
226
|
+
const status = passed === evidence.length ? 'strong' : passed > 0 ? 'improving' : 'weak';
|
|
227
|
+
return {
|
|
228
|
+
id: dimension.id,
|
|
229
|
+
label: dimension.label,
|
|
230
|
+
status,
|
|
231
|
+
points: STATUS_POINTS[status],
|
|
232
|
+
evidence,
|
|
233
|
+
};
|
|
234
|
+
});
|
|
235
|
+
const total = dimensions.reduce((sum, dimension) => sum + dimension.points, 0);
|
|
236
|
+
const max = dimensions.length * 10;
|
|
237
|
+
const weakDimensions = dimensions.filter((dimension) => dimension.status !== 'strong');
|
|
238
|
+
|
|
239
|
+
return {
|
|
240
|
+
ok: weakDimensions.length === 0,
|
|
241
|
+
score: Math.round((total / max) * 100),
|
|
242
|
+
max_score: 100,
|
|
243
|
+
dimensions,
|
|
244
|
+
next_wins: weakDimensions.length
|
|
245
|
+
? weakDimensions.map((dimension) => `Strengthen evidence for ${dimension.label}.`)
|
|
246
|
+
: ['Keep score evidence generated from repo files as new competitive surfaces are added.'],
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function evaluateEvidence(root, [type, relativePath, pattern]) {
|
|
251
|
+
const path = resolve(root, relativePath);
|
|
252
|
+
if (type === 'file') {
|
|
253
|
+
return { type, path: relativePath, ok: existsSync(path) };
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (type === 'text') {
|
|
257
|
+
if (!existsSync(path)) {
|
|
258
|
+
return { type, path: relativePath, ok: false, pattern: String(pattern) };
|
|
259
|
+
}
|
|
260
|
+
const content = readFileSync(path, 'utf8');
|
|
261
|
+
return { type, path: relativePath, ok: pattern.test(content), pattern: String(pattern) };
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return { type, path: relativePath, ok: false };
|
|
265
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const DEFAULT_MAX_AGENTS = 3;
|
|
2
|
+
|
|
3
|
+
function normalizePositiveInteger(value, fallback) {
|
|
4
|
+
const parsed = Number(value);
|
|
5
|
+
if (!Number.isFinite(parsed) || parsed < 1) {
|
|
6
|
+
return fallback;
|
|
7
|
+
}
|
|
8
|
+
return Math.floor(parsed);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function createConcurrencyPolicy({ workflow = {}, maxParallelAgentsOverride = null } = {}) {
|
|
12
|
+
const workflowParallel = workflow.parallel || {};
|
|
13
|
+
const configuredMaxAgents = normalizePositiveInteger(workflowParallel.max_agents, DEFAULT_MAX_AGENTS);
|
|
14
|
+
const hasOverride = maxParallelAgentsOverride !== null && maxParallelAgentsOverride !== undefined;
|
|
15
|
+
const maxAgents = hasOverride
|
|
16
|
+
? normalizePositiveInteger(maxParallelAgentsOverride, configuredMaxAgents)
|
|
17
|
+
: configuredMaxAgents;
|
|
18
|
+
const mode = workflowParallel.policy || 'weighted';
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
mode,
|
|
22
|
+
maxAgents,
|
|
23
|
+
source: hasOverride ? 'mission_override' : 'workflow',
|
|
24
|
+
weightFor(atomRef = {}, registry = {}) {
|
|
25
|
+
const atomDef = registry.atoms?.[atomRef.id] || {};
|
|
26
|
+
return normalizePositiveInteger(atomRef.parallel_weight ?? atomDef.parallel_weight, 1);
|
|
27
|
+
},
|
|
28
|
+
chargeFor(atomRef = {}, registry = {}) {
|
|
29
|
+
if (mode === 'fixed') {
|
|
30
|
+
return 1;
|
|
31
|
+
}
|
|
32
|
+
return Math.min(this.weightFor(atomRef, registry), maxAgents);
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { appendFile, mkdir, readFile } from 'node:fs/promises';
|
|
2
|
+
import { appendFileSync, mkdirSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { dirname, resolve } from 'node:path';
|
|
4
|
+
import { buildLogEntryHash, validateExecutionLogChain } from './workflow-integrity.js';
|
|
5
|
+
|
|
6
|
+
function nowIso() {
|
|
7
|
+
return new Date().toISOString();
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
let appendQueue = Promise.resolve();
|
|
11
|
+
|
|
12
|
+
export function resolveExecutionLogPath(projectRoot = process.cwd()) {
|
|
13
|
+
return resolve(projectRoot, '.as-xflow', 'logs', 'execution.ndjson');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function appendExecutionLog(projectRoot, entry = {}) {
|
|
17
|
+
const append = appendQueue.then(
|
|
18
|
+
() => appendExecutionLogUnlocked(projectRoot, entry),
|
|
19
|
+
() => appendExecutionLogUnlocked(projectRoot, entry),
|
|
20
|
+
);
|
|
21
|
+
appendQueue = append.catch(() => {});
|
|
22
|
+
return append;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function appendExecutionLogUnlocked(projectRoot, entry = {}) {
|
|
26
|
+
const file = resolveExecutionLogPath(projectRoot);
|
|
27
|
+
await mkdir(dirname(file), { recursive: true });
|
|
28
|
+
const previousHash = await readLastEntryHash(file);
|
|
29
|
+
const payload = {
|
|
30
|
+
timestamp: nowIso(),
|
|
31
|
+
...entry,
|
|
32
|
+
prev_hash: previousHash,
|
|
33
|
+
};
|
|
34
|
+
payload.entry_hash = buildLogEntryHash(payload);
|
|
35
|
+
await appendFile(file, `${JSON.stringify(payload)}\n`, 'utf8');
|
|
36
|
+
return file;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function appendExecutionLogSync(projectRoot, entry = {}) {
|
|
40
|
+
const file = resolveExecutionLogPath(projectRoot);
|
|
41
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
42
|
+
const previousHash = readLastEntryHashSync(file);
|
|
43
|
+
const payload = {
|
|
44
|
+
timestamp: nowIso(),
|
|
45
|
+
...entry,
|
|
46
|
+
prev_hash: previousHash,
|
|
47
|
+
};
|
|
48
|
+
payload.entry_hash = buildLogEntryHash(payload);
|
|
49
|
+
appendFileSync(file, `${JSON.stringify(payload)}\n`, 'utf8');
|
|
50
|
+
return file;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function readExecutionLog(projectRoot, options = {}) {
|
|
54
|
+
const file = resolveExecutionLogPath(projectRoot);
|
|
55
|
+
const limit = Math.max(1, Number(options.limit || 20));
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
const raw = await readFile(file, 'utf8');
|
|
59
|
+
const chronological = raw
|
|
60
|
+
.split('\n')
|
|
61
|
+
.map((line) => line.trim())
|
|
62
|
+
.filter(Boolean)
|
|
63
|
+
.map((line) => {
|
|
64
|
+
try {
|
|
65
|
+
return JSON.parse(line);
|
|
66
|
+
} catch {
|
|
67
|
+
return { kind: 'invalid_log_line', raw: line };
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
const chain = validateExecutionLogChain(chronological);
|
|
71
|
+
const entries = chronological.slice(-limit).reverse();
|
|
72
|
+
|
|
73
|
+
return { file, entries, entries_chronological: chronological, chain };
|
|
74
|
+
} catch {
|
|
75
|
+
return { file, entries: [], entries_chronological: [], chain: { ok: true, issues: [], entries_checked: 0 } };
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function readLastEntryHash(file) {
|
|
80
|
+
try {
|
|
81
|
+
const raw = await readFile(file, 'utf8');
|
|
82
|
+
return lastEntryHashFromRaw(raw);
|
|
83
|
+
} catch {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function readLastEntryHashSync(file) {
|
|
89
|
+
try {
|
|
90
|
+
const raw = readFileSync(file, 'utf8');
|
|
91
|
+
return lastEntryHashFromRaw(raw);
|
|
92
|
+
} catch {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function lastEntryHashFromRaw(raw) {
|
|
98
|
+
const lines = raw.split('\n').map((line) => line.trim()).filter(Boolean);
|
|
99
|
+
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
100
|
+
try {
|
|
101
|
+
const entry = JSON.parse(lines[index]);
|
|
102
|
+
if (entry.entry_hash) return entry.entry_hash;
|
|
103
|
+
} catch {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { resolveChangePath, writeJsonFile } from './change-artifacts.js';
|
|
2
|
+
|
|
3
|
+
const TYPE_PATTERNS = [
|
|
4
|
+
['hotfix', ['hotfix', '紧急修复', '线上问题', 'p0']],
|
|
5
|
+
['bugfix', ['bug', 'fix', '修复', '缺陷', '报错']],
|
|
6
|
+
['config-change', ['config', '配置', '开关', '阈值', '环境变量']],
|
|
7
|
+
['docs', ['doc', 'docs', 'readme', '文档', '说明']],
|
|
8
|
+
['refactor', ['refactor', '重构', '技术债', '解耦']],
|
|
9
|
+
['feature', ['feature', '新增', '功能', '需求']],
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
export function buildWorkflowRecommendation(input = {}, effectivePolicy = {}) {
|
|
13
|
+
const profile = buildDemandProfile(input);
|
|
14
|
+
const mode = effectivePolicy.execution_mode || input.execution_mode || 'personal';
|
|
15
|
+
const recommendedTrack = chooseTrack(profile);
|
|
16
|
+
const requiresCoverageGate = shouldRequireCoverage(profile, effectivePolicy);
|
|
17
|
+
const requiresHumanCheckpoint = ['critical', 'elevated'].includes(profile.risk_level)
|
|
18
|
+
|| profile.complexity === 'critical';
|
|
19
|
+
const requiresTeamNotification = mode === 'team' && (requiresHumanCheckpoint || recommendedTrack === 'corps');
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
version: 1,
|
|
23
|
+
generated_at: new Date().toISOString(),
|
|
24
|
+
recommended_track: recommendedTrack,
|
|
25
|
+
requires_coverage_gate: requiresCoverageGate,
|
|
26
|
+
requires_human_checkpoint: requiresHumanCheckpoint,
|
|
27
|
+
requires_team_notification: requiresTeamNotification,
|
|
28
|
+
profile,
|
|
29
|
+
policy_overlay: {
|
|
30
|
+
execution_mode: mode,
|
|
31
|
+
workflows_remain: ['yolo', 'corps'],
|
|
32
|
+
creates_team_workflow_fork: false,
|
|
33
|
+
},
|
|
34
|
+
reasons: buildReasons(profile, recommendedTrack, {
|
|
35
|
+
requiresCoverageGate,
|
|
36
|
+
requiresHumanCheckpoint,
|
|
37
|
+
requiresTeamNotification,
|
|
38
|
+
}),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function writeWorkflowRecommendation({ projectRoot, changeId, input = {}, effectivePolicy = {} }) {
|
|
43
|
+
const recommendation = buildWorkflowRecommendation(input, effectivePolicy);
|
|
44
|
+
const outputPath = resolveChangePath(projectRoot, changeId, 'workflow_recommendation.json');
|
|
45
|
+
writeJsonFile(outputPath, recommendation);
|
|
46
|
+
return { ok: true, recommendation, workflow_recommendation_file: outputPath };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function buildDemandProfile(input = {}) {
|
|
50
|
+
const title = String(input.title || input.requirement_title || input.req_name || '').trim();
|
|
51
|
+
const description = String(input.description || input.requirement_description || '').trim();
|
|
52
|
+
const combined = `${title} ${description}`.toLowerCase();
|
|
53
|
+
const repoCount = normalizeRepoCount(input);
|
|
54
|
+
const estimatedFileCount = Number(input.estimated_file_count || input.file_count || 1);
|
|
55
|
+
const demandType = input.demand_type || input.change_type || classifyDemandType(combined);
|
|
56
|
+
const affectsApi = hasAny(combined, ['api', '接口', 'endpoint', 'rpc', 'grpc']);
|
|
57
|
+
const affectsDatabase = hasAny(combined, ['database', 'db', '数据库', 'ddl', 'migration', 'schema']);
|
|
58
|
+
const involvesPayment = hasAny(combined, ['payment', '支付', 'pay', 'billing', '结算']);
|
|
59
|
+
const involvesUserData = hasAny(combined, ['user data', '用户数据', '隐私', '个人信息']);
|
|
60
|
+
const riskLevel = assessRisk({ involvesPayment, involvesUserData, affectsDatabase, repoCount });
|
|
61
|
+
const complexity = assessComplexity({ repoCount, estimatedFileCount, affectsApi, affectsDatabase, riskLevel });
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
demand_type: normalizeDemandType(demandType),
|
|
65
|
+
complexity,
|
|
66
|
+
risk_level: riskLevel,
|
|
67
|
+
repo_count: repoCount,
|
|
68
|
+
estimated_file_count: estimatedFileCount,
|
|
69
|
+
cross_service: repoCount > 1,
|
|
70
|
+
affects_api: affectsApi,
|
|
71
|
+
affects_database: affectsDatabase,
|
|
72
|
+
involves_payment: involvesPayment,
|
|
73
|
+
involves_user_data: involvesUserData,
|
|
74
|
+
title,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function classifyDemandType(text) {
|
|
79
|
+
for (const [type, patterns] of TYPE_PATTERNS) {
|
|
80
|
+
if (patterns.some((pattern) => text.includes(pattern))) return type;
|
|
81
|
+
}
|
|
82
|
+
return 'feature';
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function normalizeDemandType(type) {
|
|
86
|
+
if (type === 'documentation') return 'docs';
|
|
87
|
+
if (type === 'backend' || type === 'frontend' || type === 'full-stack') return 'feature';
|
|
88
|
+
return type || 'feature';
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function normalizeRepoCount(input = {}) {
|
|
92
|
+
if (Array.isArray(input.repositories)) return input.repositories.length || 1;
|
|
93
|
+
const value = Number(input.repo_count || input.repositories || 1);
|
|
94
|
+
return Number.isFinite(value) && value > 0 ? value : 1;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function assessRisk({ involvesPayment, involvesUserData, affectsDatabase, repoCount }) {
|
|
98
|
+
if (involvesPayment || involvesUserData) return 'critical';
|
|
99
|
+
if (affectsDatabase || repoCount > 1) return 'elevated';
|
|
100
|
+
return 'standard';
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function assessComplexity({ repoCount, estimatedFileCount, affectsApi, affectsDatabase, riskLevel }) {
|
|
104
|
+
if (riskLevel === 'critical' || repoCount > 3 || estimatedFileCount > 20) return 'critical';
|
|
105
|
+
if (repoCount > 1 || estimatedFileCount > 8 || affectsDatabase) return 'high';
|
|
106
|
+
if (estimatedFileCount > 3 || affectsApi) return 'medium';
|
|
107
|
+
return 'low';
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function chooseTrack(profile) {
|
|
111
|
+
if (profile.demand_type === 'docs' && profile.risk_level === 'standard') return 'yolo';
|
|
112
|
+
if (profile.risk_level === 'critical' || profile.complexity === 'critical' || profile.cross_service) return 'corps';
|
|
113
|
+
return 'yolo';
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function shouldRequireCoverage(profile, policy = {}) {
|
|
117
|
+
if (profile.demand_type === 'docs') return false;
|
|
118
|
+
if (policy?.coding?.min_changed_line_coverage !== undefined) return true;
|
|
119
|
+
return ['feature', 'bugfix', 'hotfix', 'refactor'].includes(profile.demand_type);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function buildReasons(profile, track, flags) {
|
|
123
|
+
const reasons = [`track:${track}`];
|
|
124
|
+
if (profile.cross_service) reasons.push('cross_service');
|
|
125
|
+
if (profile.risk_level !== 'standard') reasons.push(`risk:${profile.risk_level}`);
|
|
126
|
+
if (flags.requiresCoverageGate) reasons.push('coverage_gate');
|
|
127
|
+
if (flags.requiresHumanCheckpoint) reasons.push('human_checkpoint');
|
|
128
|
+
if (flags.requiresTeamNotification) reasons.push('team_notification');
|
|
129
|
+
return reasons;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function hasAny(text, patterns) {
|
|
133
|
+
return patterns.some((pattern) => text.includes(pattern));
|
|
134
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { resolveBoundMissionId } from './bindings.js';
|
|
2
|
+
|
|
3
|
+
function nowIso() {
|
|
4
|
+
return new Date().toISOString();
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function ensureArray(value) {
|
|
8
|
+
return Array.isArray(value) ? value : value ? [value] : [];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function makeId() {
|
|
12
|
+
return `mem_${Math.random().toString(36).slice(2, 14)}`;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function createMemoryItem(state, input = {}) {
|
|
16
|
+
state.memories = ensureArray(state.memories);
|
|
17
|
+
const timestamp = nowIso();
|
|
18
|
+
const item = {
|
|
19
|
+
memory_id: makeId(),
|
|
20
|
+
mission_id: input.mission_id || null,
|
|
21
|
+
scope: input.scope || (input.mission_id ? 'mission' : 'project'),
|
|
22
|
+
scope_ref: input.scope_ref || input.mission_id || input.workspace_id || 'default-workspace',
|
|
23
|
+
kind: input.kind || 'fact',
|
|
24
|
+
category: input.category || 'workflow',
|
|
25
|
+
status: input.status || 'active',
|
|
26
|
+
canonical_text: input.canonical_text || input.text || '',
|
|
27
|
+
structured_payload: input.structured_payload || {},
|
|
28
|
+
source_events: ensureArray(input.source_events),
|
|
29
|
+
evidence_refs: ensureArray(input.evidence_refs),
|
|
30
|
+
created_at: timestamp,
|
|
31
|
+
updated_at: timestamp,
|
|
32
|
+
};
|
|
33
|
+
state.memories.unshift(item);
|
|
34
|
+
return item;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function listMemoryItems(state, filter = {}) {
|
|
38
|
+
return ensureArray(state.memories).filter((item) => {
|
|
39
|
+
if (filter.mission_id && item.mission_id !== filter.mission_id) return false;
|
|
40
|
+
if (filter.scope && item.scope !== filter.scope) return false;
|
|
41
|
+
if (filter.status && item.status !== filter.status) return false;
|
|
42
|
+
return true;
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function buildMemoryMount(state, input = {}) {
|
|
47
|
+
const items = listMemoryItems(state, input).slice(0, Number(input.limit || 20));
|
|
48
|
+
return {
|
|
49
|
+
mission_id: input.mission_id || null,
|
|
50
|
+
total: items.length,
|
|
51
|
+
items,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function resolveMemoryMissionId(input = {}, context = {}) {
|
|
56
|
+
return resolveBoundMissionId({
|
|
57
|
+
projectRoot: context.projectRoot || process.cwd(),
|
|
58
|
+
changeId: context.changeId || input.change_id || null,
|
|
59
|
+
missionId: input.mission_id || null,
|
|
60
|
+
runtimeMissionId: context.runtime?.missionId || null,
|
|
61
|
+
});
|
|
62
|
+
}
|