aw-spec-cli 0.1.32 → 0.1.34
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/README.md +76 -81
- package/assets/.appweaver/DESIGN-FORMAT.md +56 -17
- package/assets/.appweaver/STACK-PROFILES.md +95 -60
- package/assets/.appweaver/orchestrator/LAUNCHER.md +1 -1
- package/assets/.appweaver/orchestrator/modules/delta-plan.orch.yaml +173 -32
- package/assets/.appweaver/orchestrator/modules/domain-select.orch.yaml +29 -1
- package/assets/.appweaver/orchestrator/modules/foundation-pack.orch.yaml +7 -4
- package/assets/.appweaver/orchestrator/modules/foundation-spec.orch.yaml +49 -60
- package/assets/.appweaver/orchestrator/modules/tech-select.orch.yaml +58 -0
- package/assets/.appweaver/orchestrator/orchestrator.py +26 -8
- package/assets/.appweaver/orchestrator/rev-templates/delta-design-template.skeleton.md +20 -16
- package/assets/.appweaver/orchestrator/rev-templates/design-template.skeleton.md +30 -6
- package/assets/.appweaver/orchestrator/rev-templates/ui-delta-design-template.skeleton.md +9 -7
- package/assets/.appweaver/orchestrator/rev-templates/ui-design-template.skeleton.md +5 -5
- package/assets/.appweaver/orchestrator/workflows/aw-spec-adopt.orch.yaml +341 -36
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan-delta.orch.yaml +254 -43
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan.orch.yaml +248 -68
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice-delta.orch.yaml +251 -20
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +316 -10
- package/assets/.appweaver/orchestrator/workflows/aw-spec-build.orch.yaml +6 -47
- package/assets/.appweaver/orchestrator/workflows/aw-spec-close-delta.orch.yaml +62 -16
- package/assets/.appweaver/orchestrator/workflows/aw-spec-finalize-delta.orch.yaml +132 -9
- package/assets/.appweaver/orchestrator/workflows/aw-spec-fix-test-coverage.orch.yaml +965 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +222 -169
- package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +28 -12
- package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +453 -116
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-lane.orch.yaml +7 -4
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-stack.orch.yaml +83 -6
- package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +45 -2
- package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +324 -74
- package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +275 -44
- package/assets/.appweaver/scripts/python/aw-foundation.py +16 -13
- package/assets/.appweaver/scripts/python/aw-gaps.py +11 -3
- package/assets/.appweaver/scripts/python/aw-model-guard.py +295 -0
- package/assets/.appweaver/scripts/python/aw-plan-coverage.py +412 -3
- package/assets/.appweaver/scripts/python/aw-plangate.py +23 -16
- package/assets/.appweaver/scripts/python/aw-stack.py +1794 -265
- package/assets/.appweaver/stack-profiles/dotnet-webapi.json +0 -2
- package/assets/.appweaver/stack-profiles/java-spring.json +0 -2
- package/assets/.appweaver/templates/api-plan-template.md +107 -0
- package/assets/.appweaver/templates/delta-api-plan-template.md +87 -0
- package/assets/.appweaver/templates/delta-design-template.md +25 -23
- package/assets/.appweaver/templates/delta-spec-template.md +3 -3
- package/assets/.appweaver/templates/design-template.md +20 -42
- package/assets/.appweaver/templates/domain-model-template.md +3 -0
- package/assets/.appweaver/templates/ui-delta-design-template.md +11 -8
- package/assets/.appweaver/templates/ui-design-template.md +4 -4
- package/assets/foundations/foundation.spec.template.txt +25 -18
- package/assets/guardrails/README.md +61 -25
- package/assets/guardrails/methodology/ux/accessibility-standards.md +38 -0
- package/assets/guardrails/methodology/ux/content-design.md +34 -0
- package/assets/guardrails/methodology/ux/interaction-states.md +45 -0
- package/assets/guardrails/methodology/ux/journey-mapping.md +41 -0
- package/assets/guardrails/project-specific/domain/domain-requirements.md +25 -38
- package/assets/guardrails/project-specific/domain-rule-bindings.md +33 -69
- package/assets/guardrails/project-specific/frontend/ui-requirements.md +2 -2
- package/assets/guardrails/project-specific/service/technical-requirements.md +2 -2
- package/assets/guardrails/project-specific/tech-rule-bindings.md +16 -13
- package/assets/guardrails/sample-healthcare/domain/domain-requirements.md +58 -0
- package/assets/guardrails/sample-healthcare/domain-rule-bindings.md +138 -0
- package/assets/guardrails/sample-healthcare/frontend/ui-requirements.md +43 -0
- package/assets/guardrails/sample-healthcare/service/technical-requirements.md +40 -0
- package/assets/guardrails/sample-healthcare/tech-rule-bindings.md +303 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +216 -7
- package/dist/commands/init.js.map +1 -1
- package/dist/utils/config.js +1 -1
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/foundation.d.ts +36 -0
- package/dist/utils/foundation.d.ts.map +1 -0
- package/dist/utils/foundation.js +109 -0
- package/dist/utils/foundation.js.map +1 -0
- package/dist/utils/paths.js +1 -1
- package/dist/utils/paths.js.map +1 -1
- package/package.json +1 -1
|
@@ -110,23 +110,32 @@ function Invoke-ResolveDomain {
|
|
|
110
110
|
$rulemap = Join-Path $repo "guardrails/$domain/domain-rule-bindings.md"
|
|
111
111
|
$rulemapExists = ($domain -ne '') -and (Test-Path $rulemap)
|
|
112
112
|
|
|
113
|
-
|
|
113
|
+
# A mapped file that is not on disk goes to `missing`, never to `candidates`, so a
|
|
114
|
+
# caller can say so instead of loading nothing in its place without a word.
|
|
115
|
+
# heading_found tells a heading that is deliberately empty (a `_None_` line) apart
|
|
116
|
+
# from one that is absent or misspelled — both give no candidates.
|
|
117
|
+
$candidates = @(); $missing = @(); $headingFound = $false
|
|
114
118
|
if ($rulemapExists) {
|
|
115
119
|
$lines = Get-Content $rulemap
|
|
116
120
|
$heading = "## /$commandName"
|
|
117
121
|
$grab = $false; $found = @()
|
|
118
122
|
foreach ($line in $lines) {
|
|
119
|
-
if ($line -eq $heading) { $grab = $true; continue }
|
|
123
|
+
if ($line -eq $heading) { $grab = $true; $headingFound = $true; continue }
|
|
120
124
|
if ($line -match '^## ') { $grab = $false }
|
|
121
|
-
|
|
125
|
+
# A path written as code (- `path`) is read without its backticks.
|
|
126
|
+
if ($grab -and $line -match '^\s*-\s+(.+?)\s*$') { $found += ($Matches[1] -replace '^`([^`]+)`$', '$1') }
|
|
127
|
+
}
|
|
128
|
+
foreach ($f in $found) {
|
|
129
|
+
if (Test-Path -LiteralPath (Join-Path $repo $f) -PathType Leaf) { $candidates += $f } else { $missing += $f }
|
|
122
130
|
}
|
|
123
|
-
if ($found.Count -gt 0) { $candidates = $found }
|
|
124
131
|
}
|
|
125
132
|
|
|
126
133
|
Write-Json ([ordered]@{
|
|
127
134
|
domain = $domain
|
|
128
135
|
rulemap_exists = $rulemapExists
|
|
136
|
+
heading_found = $headingFound
|
|
129
137
|
candidates = @($candidates)
|
|
138
|
+
missing = @($missing)
|
|
130
139
|
})
|
|
131
140
|
}
|
|
132
141
|
|
|
@@ -175,21 +184,27 @@ function Invoke-ResolveTech {
|
|
|
175
184
|
$subs['stack'] = $subs['backend_stack'] # the unqualified form means the service lane
|
|
176
185
|
$declared = @($subs.Values | Where-Object { $_ } | Select-Object -Unique)
|
|
177
186
|
|
|
178
|
-
|
|
187
|
+
# Whether the command's heading is in the map at all. A heading with no bullets (a
|
|
188
|
+
# `_None_` line) is a deliberate choice; an absent or misspelled one loads the same
|
|
189
|
+
# nothing, and only this tells the caller which of the two it is.
|
|
190
|
+
$bullets = @(); $headingFound = $false
|
|
179
191
|
if ($techmapExists) {
|
|
180
192
|
$lines = Get-Content $techmap
|
|
181
193
|
foreach ($heading in @("## /$commandName", "## /$($commandName -replace 'decl-','')")) {
|
|
182
194
|
$grab = $false; $found = @()
|
|
183
195
|
foreach ($line in $lines) {
|
|
184
|
-
if ($line -eq $heading) { $grab = $true; continue }
|
|
196
|
+
if ($line -eq $heading) { $grab = $true; $headingFound = $true; continue }
|
|
185
197
|
if ($line -match '^## ') { $grab = $false }
|
|
186
|
-
|
|
198
|
+
# A path written as code (- `path`) is read without its backticks.
|
|
199
|
+
if ($grab -and $line -match '^\s*-\s+(.+?)\s*$') { $found += ($Matches[1] -replace '^`([^`]+)`$', '$1') }
|
|
187
200
|
}
|
|
188
201
|
if ($found.Count -gt 0) { $bullets = $found; break }
|
|
189
202
|
}
|
|
190
203
|
}
|
|
191
204
|
|
|
192
|
-
|
|
205
|
+
# A placeholder the kit does not know is a typo in the map, unlike one for a lane
|
|
206
|
+
# this repo does not declare, which is skipped because its rules do not apply.
|
|
207
|
+
$candidates = @(); $missing = @(); $skipped = @(); $mismatched = @(); $unknown = @()
|
|
193
208
|
foreach ($b in $bullets) {
|
|
194
209
|
$holes = @([regex]::Matches($b, '\{\{\s*([a-z_]+)\s*\}\}') | ForEach-Object { $_.Groups[1].Value })
|
|
195
210
|
$why = ''
|
|
@@ -199,7 +214,11 @@ function Invoke-ResolveTech {
|
|
|
199
214
|
# genuinely does not exist, so its rules genuinely do not apply.
|
|
200
215
|
if (-not $subs[$h]) { $why = "no lane declared for {{$h}}"; break }
|
|
201
216
|
}
|
|
202
|
-
if ($why) {
|
|
217
|
+
if ($why) {
|
|
218
|
+
$skipped += [ordered]@{ bullet = $b; why = $why }
|
|
219
|
+
if ($why -like 'unknown placeholder*') { $unknown += $b }
|
|
220
|
+
continue
|
|
221
|
+
}
|
|
203
222
|
$path = [regex]::Replace($b, '\{\{\s*([a-z_]+)\s*\}\}', { param($m) $subs[$m.Groups[1].Value] })
|
|
204
223
|
if (Test-Path (Join-Path $repo $path)) { $candidates += $path } else { $missing += $path }
|
|
205
224
|
# A LITERAL stack directory that is not one this repo declares. Reported, never
|
|
@@ -212,13 +231,30 @@ function Invoke-ResolveTech {
|
|
|
212
231
|
}
|
|
213
232
|
}
|
|
214
233
|
|
|
234
|
+
# A STACK WITH NO RULE FOLDER AT ALL — reported as the folder, see the bash twin. A file
|
|
235
|
+
# missing from a folder that does exist stays listed in missing_elsewhere.
|
|
236
|
+
$absent = @(); $absentFiles = 0; $elsewhere = @()
|
|
237
|
+
foreach ($p in $missing) {
|
|
238
|
+
$m3 = [regex]::Match($p, '^(guardrails/technology/[^/]+/)')
|
|
239
|
+
if ($m3.Success -and -not (Test-Path -LiteralPath (Join-Path $repo $m3.Groups[1].Value) -PathType Container)) {
|
|
240
|
+
if ($absent -notcontains $m3.Groups[1].Value) { $absent += $m3.Groups[1].Value }
|
|
241
|
+
$absentFiles++
|
|
242
|
+
} else { $elsewhere += $p }
|
|
243
|
+
}
|
|
244
|
+
|
|
215
245
|
Write-Json ([ordered]@{
|
|
216
|
-
domain
|
|
217
|
-
techrulemap_exists
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
246
|
+
domain = $domain
|
|
247
|
+
techrulemap_exists = $techmapExists
|
|
248
|
+
heading_found = $headingFound
|
|
249
|
+
candidates = @($candidates)
|
|
250
|
+
missing = @($missing)
|
|
251
|
+
missing_elsewhere = @($elsewhere)
|
|
252
|
+
absent_corpora = @($absent)
|
|
253
|
+
absent_corpora_text = ($absent -join ' and ')
|
|
254
|
+
absent_files = $absentFiles
|
|
255
|
+
skipped = @($skipped)
|
|
256
|
+
unknown_placeholders = @($unknown)
|
|
257
|
+
declared_stacks = @($declared | Sort-Object)
|
|
222
258
|
stack_mismatch = ($mismatched.Count -gt 0)
|
|
223
259
|
mismatched_stacks = @($mismatched)
|
|
224
260
|
})
|
|
@@ -579,6 +615,16 @@ function Invoke-AwStackCore {
|
|
|
579
615
|
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
|
580
616
|
}
|
|
581
617
|
|
|
618
|
+
# One aw-stack call whose JSON the caller reads instead of printing; $null when it fails.
|
|
619
|
+
function Get-AwStackJson {
|
|
620
|
+
param([string]$Op, [string[]]$Rest)
|
|
621
|
+
$py = Get-Py
|
|
622
|
+
if (-not $py) { return $null }
|
|
623
|
+
$core = Join-Path $PSScriptRoot '..' 'python' 'aw-stack.py'
|
|
624
|
+
try { return ((& $py $core '--repo' (Get-RepoRoot) $Op @Rest) | Out-String | ConvertFrom-Json) }
|
|
625
|
+
catch { return $null }
|
|
626
|
+
}
|
|
627
|
+
|
|
582
628
|
function Invoke-StackList {
|
|
583
629
|
param([string[]]$Rest)
|
|
584
630
|
$fwd = @()
|
|
@@ -608,6 +654,20 @@ function Invoke-StackDetect {
|
|
|
608
654
|
Invoke-AwStackCore -Op 'detect' -Rest $fwd
|
|
609
655
|
}
|
|
610
656
|
|
|
657
|
+
# Which stacks the active project's technical map has rule files for — see the bash twin.
|
|
658
|
+
function Invoke-StackRules {
|
|
659
|
+
param([string[]]$Rest)
|
|
660
|
+
$fwd = @()
|
|
661
|
+
for ($i = 0; $i -lt $Rest.Count; $i++) {
|
|
662
|
+
switch ($Rest[$i]) {
|
|
663
|
+
'--kind' { $fwd += '--kind'; $fwd += $Rest[++$i] }
|
|
664
|
+
'--declared' { $fwd += '--declared' }
|
|
665
|
+
default { Write-Error "stack-rules: unknown arg '$($Rest[$i])'"; exit 1 }
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
Invoke-AwStackCore -Op 'rules' -Rest $fwd
|
|
669
|
+
}
|
|
670
|
+
|
|
611
671
|
function Invoke-LaneDeclare {
|
|
612
672
|
param([string[]]$Rest)
|
|
613
673
|
$fwd = @()
|
|
@@ -640,8 +700,8 @@ function Invoke-LaneResolve {
|
|
|
640
700
|
Invoke-AwStackCore -Op 'resolve' -Rest $fwd
|
|
641
701
|
}
|
|
642
702
|
|
|
643
|
-
# Change the API-contract keys (contract_run, contract, client) on a lane
|
|
644
|
-
# declared. Twin of bash cmd_lane_set.
|
|
703
|
+
# Change the API-contract keys (contract_write, contract_run, contract, client) on a lane
|
|
704
|
+
# already declared. Twin of bash cmd_lane_set.
|
|
645
705
|
function Invoke-LaneSet {
|
|
646
706
|
param([string[]]$Rest)
|
|
647
707
|
$fwd = @()
|
|
@@ -654,8 +714,9 @@ function Invoke-LaneSet {
|
|
|
654
714
|
Invoke-AwStackCore -Op 'set' -Rest $fwd
|
|
655
715
|
}
|
|
656
716
|
|
|
657
|
-
#
|
|
658
|
-
# Twins of the bash cmd_contract_export / _draft / _combine / _requests / _client / _check
|
|
717
|
+
# ─────── contract-export / -draft / -combine / -requests / -client / -check / -verify / -merge, api-split ───────
|
|
718
|
+
# Twins of the bash cmd_contract_export / _draft / _combine / _requests / _client / _check /
|
|
719
|
+
# _verify / _merge and cmd_api_split.
|
|
659
720
|
function Invoke-ContractExport {
|
|
660
721
|
param([string[]]$Rest)
|
|
661
722
|
$fwd = @()
|
|
@@ -668,12 +729,19 @@ function Invoke-ContractExport {
|
|
|
668
729
|
Invoke-AwStackCore -Op 'contract-export' -Rest $fwd
|
|
669
730
|
}
|
|
670
731
|
|
|
732
|
+
# Each value is forwarded only when it is not empty — PowerShell 5.1 drops an empty
|
|
733
|
+
# argument, and the flag would then take the next one.
|
|
671
734
|
function Invoke-ContractDraft {
|
|
672
735
|
param([string[]]$Rest)
|
|
673
736
|
$fwd = @()
|
|
674
737
|
for ($i = 0; $i -lt $Rest.Count; $i++) {
|
|
675
738
|
switch ($Rest[$i]) {
|
|
676
|
-
|
|
739
|
+
{ $_ -in '--feature-dir','--cr' } {
|
|
740
|
+
$flag = $Rest[$i]
|
|
741
|
+
if ($i + 1 -lt $Rest.Count -and -not "$($Rest[$i + 1])".StartsWith('--')) {
|
|
742
|
+
$v = $Rest[++$i]; if ($v) { $fwd += $flag; $fwd += $v }
|
|
743
|
+
}
|
|
744
|
+
}
|
|
677
745
|
default { Write-Error "contract-draft: unknown arg '$($Rest[$i])'"; exit 1 }
|
|
678
746
|
}
|
|
679
747
|
}
|
|
@@ -744,6 +812,67 @@ function Invoke-ContractImpact {
|
|
|
744
812
|
Invoke-AwStackCore -Op 'contract-impact' -Rest $fwd
|
|
745
813
|
}
|
|
746
814
|
|
|
815
|
+
# cmd_contract_verify: does the built service serve what its plan names; with --check, only
|
|
816
|
+
# whether the plan names anything to check. Each value is forwarded only when it is not
|
|
817
|
+
# empty, as above.
|
|
818
|
+
function Invoke-ContractVerify {
|
|
819
|
+
param([string[]]$Rest)
|
|
820
|
+
$fwd = @()
|
|
821
|
+
for ($i = 0; $i -lt $Rest.Count; $i++) {
|
|
822
|
+
switch ($Rest[$i]) {
|
|
823
|
+
{ $_ -in '--feature-dir','--cr','--doc' } {
|
|
824
|
+
$flag = $Rest[$i]
|
|
825
|
+
if ($i + 1 -lt $Rest.Count -and -not "$($Rest[$i + 1])".StartsWith('--')) {
|
|
826
|
+
$v = $Rest[++$i]; if ($v) { $fwd += $flag; $fwd += $v }
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
'--check' { $fwd += '--check' }
|
|
830
|
+
default { Write-Error "contract-verify: unknown arg '$($Rest[$i])'"; exit 1 }
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
Invoke-AwStackCore -Op 'contract-verify' -Rest $fwd
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
# cmd_contract_merge: a change's delta API plan, merged into its feature's API plan; with
|
|
837
|
+
# --check, only whether it is still to merge (close-delta).
|
|
838
|
+
function Invoke-ContractMerge {
|
|
839
|
+
param([string[]]$Rest)
|
|
840
|
+
$fwd = @()
|
|
841
|
+
for ($i = 0; $i -lt $Rest.Count; $i++) {
|
|
842
|
+
switch ($Rest[$i]) {
|
|
843
|
+
{ $_ -in '--feature-dir','--cr' } {
|
|
844
|
+
$flag = $Rest[$i]
|
|
845
|
+
if ($i + 1 -lt $Rest.Count -and -not "$($Rest[$i + 1])".StartsWith('--')) {
|
|
846
|
+
$v = $Rest[++$i]; if ($v) { $fwd += $flag; $fwd += $v }
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
'--check' { $fwd += '--check' }
|
|
850
|
+
default { Write-Error "contract-merge: unknown arg '$($Rest[$i])'"; exit 1 }
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
Invoke-AwStackCore -Op 'contract-merge' -Rest $fwd
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
# cmd_api_split: a feature's endpoints and requests table, moved out of its backend plan
|
|
857
|
+
# into an API plan; --check only reports whether one is still to move.
|
|
858
|
+
function Invoke-ApiSplit {
|
|
859
|
+
param([string[]]$Rest)
|
|
860
|
+
$fwd = @()
|
|
861
|
+
for ($i = 0; $i -lt $Rest.Count; $i++) {
|
|
862
|
+
switch ($Rest[$i]) {
|
|
863
|
+
{ $_ -in '--feature-dir','--cr' } {
|
|
864
|
+
$flag = $Rest[$i]
|
|
865
|
+
if ($i + 1 -lt $Rest.Count -and -not "$($Rest[$i + 1])".StartsWith('--')) {
|
|
866
|
+
$v = $Rest[++$i]; if ($v) { $fwd += $flag; $fwd += $v }
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
'--check' { $fwd += '--check' }
|
|
870
|
+
default { Write-Error "api-split: unknown arg '$($Rest[$i])'"; exit 1 }
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
Invoke-AwStackCore -Op 'api-split' -Rest $fwd
|
|
874
|
+
}
|
|
875
|
+
|
|
747
876
|
# The gate: is the declaration USABLE for the phase about to run. Twin of bash
|
|
748
877
|
# cmd_lane_verify. --require names a lane kind the phase cannot run without;
|
|
749
878
|
# --allow-missing-root is the greenfield state, where a lane root folder is declared
|
|
@@ -1493,6 +1622,10 @@ function Invoke-FeaturePaths {
|
|
|
1493
1622
|
$uiDesign = Join-Path $fd 'frontend-plan.md'
|
|
1494
1623
|
if (-not (Test-Path $design) -and (Test-Path (Join-Path $fd 'design.md'))) { $design = Join-Path $fd 'design.md' }
|
|
1495
1624
|
if (-not (Test-Path $uiDesign) -and (Test-Path (Join-Path $fd 'design.ui.md'))) { $uiDesign = Join-Path $fd 'design.ui.md' }
|
|
1625
|
+
# The API plan beside the backend plan: the service's published endpoints, a master that
|
|
1626
|
+
# finalize-delta merges each change's API into. A feature planned before it existed has
|
|
1627
|
+
# none, and keeps its endpoints in its backend plan until `api-split` moves them.
|
|
1628
|
+
$apiPlan = Join-Path $fd 'api-plan.md'
|
|
1496
1629
|
# needs_bootstrap, not spec_exists, is what picks specify's branch. A crashed first run
|
|
1497
1630
|
# leaves spec.md on disk with the marker still up, and branching on the file alone read
|
|
1498
1631
|
# that as a spec to EXTEND — re-applying the same input over an unfinished v1.0.
|
|
@@ -1511,6 +1644,8 @@ function Invoke-FeaturePaths {
|
|
|
1511
1644
|
frontend_plan = $uiDesign
|
|
1512
1645
|
backend_plan_exists = (Test-Path $design)
|
|
1513
1646
|
frontend_plan_exists = (Test-Path $uiDesign)
|
|
1647
|
+
api_plan = $apiPlan
|
|
1648
|
+
api_plan_exists = (Test-Path $apiPlan)
|
|
1514
1649
|
design_file = $design
|
|
1515
1650
|
ui_design_file = $uiDesign
|
|
1516
1651
|
spec_exists = $specExists
|
|
@@ -2602,14 +2737,17 @@ function Invoke-DesignScaffold {
|
|
|
2602
2737
|
# backend / frontend are the PLAN-phase pairs: the same templates, the plan document
|
|
2603
2738
|
# names, and a MIGRATION — a legacy design.md / design.ui.md is RENAMED into the plan
|
|
2604
2739
|
# name, so a feature never carries two spellings of one master. service / ui stay
|
|
2605
|
-
# byte-identical for the callers that still write the legacy names.
|
|
2740
|
+
# byte-identical for the callers that still write the legacy names. api is the backend
|
|
2741
|
+
# lane's API plan, beside its plan — one template for every project, since its endpoint
|
|
2742
|
+
# block form is fixed.
|
|
2606
2743
|
$legacyName = ''
|
|
2607
2744
|
switch ($lane) {
|
|
2608
2745
|
'service' { $docName = 'design.md'; if (-not $tplName) { $tplName = 'design-template' } }
|
|
2609
2746
|
'ui' { $docName = 'design.ui.md'; if (-not $tplName) { $tplName = 'ui-design-template' } }
|
|
2610
2747
|
'backend' { $docName = 'backend-plan.md'; $legacyName = 'design.md'; if (-not $tplName) { $tplName = 'design-template' } }
|
|
2611
2748
|
'frontend' { $docName = 'frontend-plan.md'; $legacyName = 'design.ui.md'; if (-not $tplName) { $tplName = 'ui-design-template' } }
|
|
2612
|
-
|
|
2749
|
+
'api' { $docName = 'api-plan.md'; if (-not $tplName) { $tplName = 'api-plan-template' } }
|
|
2750
|
+
default { Write-Error ("design-scaffold: unknown --lane '" + $lane + "' (backend|frontend|api|service|ui)"); exit 1 }
|
|
2613
2751
|
}
|
|
2614
2752
|
$repo = Get-RepoRoot
|
|
2615
2753
|
$fd = Resolve-FeatureDir -Arg $fdArg -RepoRoot $repo
|
|
@@ -2623,7 +2761,15 @@ function Invoke-DesignScaffold {
|
|
|
2623
2761
|
if ((-not (Test-Path $target)) -and $legacyName -and (Test-Path (Join-Path $fd $legacyName))) {
|
|
2624
2762
|
Move-Item (Join-Path $fd $legacyName) $target; $migrated = $true
|
|
2625
2763
|
}
|
|
2626
|
-
|
|
2764
|
+
# An API plan is never scaffolded beside a backend plan that still carries the API: the
|
|
2765
|
+
# empty template would be read in its place and the draft would lose every endpoint.
|
|
2766
|
+
# `api-split` moves them first.
|
|
2767
|
+
$blocked = $false
|
|
2768
|
+
if ($lane -eq 'api' -and -not (Test-Path $target)) {
|
|
2769
|
+
$chk = Get-AwStackJson -Op 'api-split' -Rest @('--feature-dir', $fd, '--check')
|
|
2770
|
+
$blocked = [bool]($chk -and $chk.needed)
|
|
2771
|
+
}
|
|
2772
|
+
if ((Test-Path $target) -or $blocked) { $created = $false }
|
|
2627
2773
|
else {
|
|
2628
2774
|
$tpl = Resolve-NamedTemplate -Name $tplName -RepoRoot $repo
|
|
2629
2775
|
if ($tpl -and (Test-Path $tpl)) { Copy-Item $tpl $target } else { New-Item -ItemType File -Force -Path $target | Out-Null }
|
|
@@ -2636,7 +2782,7 @@ function Invoke-DesignScaffold {
|
|
|
2636
2782
|
$overrideActive = Test-Path (Join-Path $repo (".appweaver/templates/overrides/" + $tplName + ".md"))
|
|
2637
2783
|
$templateSource = Resolve-NamedTemplate -Name $tplName -RepoRoot $repo
|
|
2638
2784
|
$tsOut = if ($templateSource) { $templateSource } else { '' }
|
|
2639
|
-
Write-Json ([ordered]@{ feature_dir = $fd; design_doc = $target; plan_doc = $target; lane = $lane; created = $created; migrated = $migrated; override_active = $overrideActive; template = $tplName; template_source = $tsOut })
|
|
2785
|
+
Write-Json ([ordered]@{ feature_dir = $fd; design_doc = $target; plan_doc = $target; lane = $lane; created = $created; migrated = $migrated; blocked = $blocked; override_active = $overrideActive; template = $tplName; template_source = $tsOut })
|
|
2640
2786
|
}
|
|
2641
2787
|
|
|
2642
2788
|
# ─────────────────────────── delta-plan-scaffold ───────────────────────────
|
|
@@ -2665,7 +2811,9 @@ function Invoke-DeltaPlanScaffold {
|
|
|
2665
2811
|
if (-not $tplName) { $tplName = 'delta-design-template' } }
|
|
2666
2812
|
'frontend' { $docName = 'delta-frontend-plan.md'; $legacyName = 'delta-design.ui.md'
|
|
2667
2813
|
if (-not $tplName) { $tplName = 'ui-delta-design-template' } }
|
|
2668
|
-
|
|
2814
|
+
'api' { $docName = 'delta-api-plan.md'; $legacyName = ''
|
|
2815
|
+
if (-not $tplName) { $tplName = 'delta-api-plan-template' } }
|
|
2816
|
+
default { Write-Error ("delta-plan-scaffold: unknown --lane '" + $lane + "' (backend|frontend|api)"); exit 1 }
|
|
2669
2817
|
}
|
|
2670
2818
|
$repo = Get-RepoRoot; $fd = Resolve-FeatureDir -Arg $fdArg -RepoRoot $repo
|
|
2671
2819
|
if (-not $fd) { Write-Error 'delta-plan-scaffold: no feature set and no --feature-dir'; exit 1 }
|
|
@@ -2673,10 +2821,17 @@ function Invoke-DeltaPlanScaffold {
|
|
|
2673
2821
|
if (-not (Test-Path $crDir)) { Write-Error ("delta-plan-scaffold: no such open change '" + $crId + "' (looked in " + $crDir + ")"); exit 1 }
|
|
2674
2822
|
$target = Join-Path $crDir $docName
|
|
2675
2823
|
$created = $false; $migrated = $false
|
|
2676
|
-
if ((-not (Test-Path $target)) -and (Test-Path (Join-Path $crDir $legacyName))) {
|
|
2824
|
+
if ((-not (Test-Path $target)) -and $legacyName -and (Test-Path (Join-Path $crDir $legacyName))) {
|
|
2677
2825
|
Move-Item (Join-Path $crDir $legacyName) $target; $migrated = $true
|
|
2678
2826
|
}
|
|
2679
|
-
|
|
2827
|
+
# A change planned before the delta API plan existed keeps its API in its delta backend
|
|
2828
|
+
# plan, which is read in its place; an empty delta API plan beside it would hide it.
|
|
2829
|
+
$blocked = $false
|
|
2830
|
+
if ($lane -eq 'api' -and -not (Test-Path $target)) {
|
|
2831
|
+
$chk = Get-AwStackJson -Op 'api-split' -Rest @('--feature-dir', $fd, '--cr', $crId)
|
|
2832
|
+
$blocked = [bool]($chk -and $chk.needed)
|
|
2833
|
+
}
|
|
2834
|
+
if ((-not (Test-Path $target)) -and -not $blocked) {
|
|
2680
2835
|
$tpl = Resolve-NamedTemplate -Name $tplName -RepoRoot $repo
|
|
2681
2836
|
if ($tpl -and (Test-Path $tpl)) { Copy-Item $tpl $target } else { New-Item -ItemType File -Force -Path $target | Out-Null }
|
|
2682
2837
|
$created = $true
|
|
@@ -2686,11 +2841,11 @@ function Invoke-DeltaPlanScaffold {
|
|
|
2686
2841
|
# readable wave the template's `[N]` is left standing, which is what the merge and recall
|
|
2687
2842
|
# fences read as "not ordered yet" and refuse on, with the command that stamps it.
|
|
2688
2843
|
$scaffoldWave = Get-WaveOfReadable -FeatureDir $fd -CrId $crId -Repo $repo
|
|
2689
|
-
if ($scaffoldWave) { Set-HeaderStamp -Path $target -Name 'Wave' -Value $scaffoldWave }
|
|
2844
|
+
if ($scaffoldWave -and (Test-Path $target)) { Set-HeaderStamp -Path $target -Name 'Wave' -Value $scaffoldWave }
|
|
2690
2845
|
$overrideActive = Test-Path (Join-Path $repo (".appweaver/templates/overrides/" + $tplName + ".md"))
|
|
2691
2846
|
$templateSource = Resolve-NamedTemplate -Name $tplName -RepoRoot $repo
|
|
2692
2847
|
$tsOut = if ($templateSource) { $templateSource } else { '' }
|
|
2693
|
-
Write-Json ([ordered]@{ cr_id = $crId; cr_dir = $crDir; lane = $lane; delta_plan = $target; created = $created; migrated = $migrated; override_active = $overrideActive; template = $tplName; template_source = $tsOut })
|
|
2848
|
+
Write-Json ([ordered]@{ cr_id = $crId; cr_dir = $crDir; lane = $lane; delta_plan = $target; created = $created; migrated = $migrated; blocked = $blocked; override_active = $overrideActive; template = $tplName; template_source = $tsOut })
|
|
2694
2849
|
}
|
|
2695
2850
|
|
|
2696
2851
|
# Read one field out of the lane model. Twin of the bash inline python readers: the
|
|
@@ -3204,6 +3359,29 @@ function Invoke-ModelScaffold {
|
|
|
3204
3359
|
})
|
|
3205
3360
|
}
|
|
3206
3361
|
|
|
3362
|
+
# ──────────────────── model-guard-snapshot / model-guard-check ─────────────────────
|
|
3363
|
+
# Bracket a whole-file rewrite of the context's domain-model.md (specify bootstrap, adopt):
|
|
3364
|
+
# the snapshot copies the model aside, the check compares the rewrite with the copy and, on
|
|
3365
|
+
# a loss, restores the model and keeps the rewrite as domain-model.rejected.md. THE RULE
|
|
3366
|
+
# LIVES IN python/aw-model-guard.py, which both shells invoke — see the bash twin. Both
|
|
3367
|
+
# exit 0 with `ok` in the JSON; the workflow's gate owns the stop.
|
|
3368
|
+
function Invoke-ModelGuard {
|
|
3369
|
+
param([string]$Op, [string[]]$Rest)
|
|
3370
|
+
$model = ''
|
|
3371
|
+
for ($i = 0; $i -lt $Rest.Count; $i++) {
|
|
3372
|
+
if ($Rest[$i] -in '--model','-Model') { $model = $Rest[++$i] }
|
|
3373
|
+
}
|
|
3374
|
+
if (-not $model) { Write-Error "model-guard-${Op}: --model required"; exit 1 }
|
|
3375
|
+
$repo = Get-RepoRoot
|
|
3376
|
+
if (-not [System.IO.Path]::IsPathRooted($model)) { $model = Join-Path $repo $model }
|
|
3377
|
+
$py = Get-Py
|
|
3378
|
+
if (-not $py) { Write-Error "model-guard-${Op}: python3 is required"; exit 1 }
|
|
3379
|
+
$core = Join-Path $PSScriptRoot '..' 'python' 'aw-model-guard.py'
|
|
3380
|
+
if (-not (Test-Path -LiteralPath $core)) { Write-Error "model-guard-${Op}: core not found at $core"; exit 1 }
|
|
3381
|
+
& $py $core $Op --model $model --root $repo
|
|
3382
|
+
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
|
3383
|
+
}
|
|
3384
|
+
|
|
3207
3385
|
# ─────────────────────────── cr-list ───────────────────────────────────────
|
|
3208
3386
|
# The changes currently OPEN on a feature, in wave order. This is what lets a delta stage
|
|
3209
3387
|
# ASK which change it is working on instead of inferring one: many may be open at once,
|
|
@@ -3404,6 +3582,7 @@ function Invoke-CrPaths {
|
|
|
3404
3582
|
$dmodel = Join-Path $ctx "domain-model.$crId.delta.md"
|
|
3405
3583
|
$be = Join-Path $crDir 'delta-backend-plan.md'
|
|
3406
3584
|
$fe = Join-Path $crDir 'delta-frontend-plan.md'
|
|
3585
|
+
$api = Join-Path $crDir 'delta-api-plan.md'
|
|
3407
3586
|
$master = Join-Path $ctx 'domain-model.md'
|
|
3408
3587
|
$wave = [int](Get-WaveOf -FeatureDir $fd -CrId $crId -Repo $repo)
|
|
3409
3588
|
$recorded = @(Get-ChangeLanes -Path $dspec)
|
|
@@ -3445,6 +3624,8 @@ function Invoke-CrPaths {
|
|
|
3445
3624
|
delta_model = (ConvertTo-RepoRelative -Path $dmodel -RepoRoot $repo); delta_model_exists = (Test-Path $dmodel)
|
|
3446
3625
|
delta_backend_plan = (ConvertTo-RepoRelative -Path $be -RepoRoot $repo); delta_backend_plan_exists = (Test-Path $be)
|
|
3447
3626
|
delta_frontend_plan = (ConvertTo-RepoRelative -Path $fe -RepoRoot $repo); delta_frontend_plan_exists = (Test-Path $fe)
|
|
3627
|
+
# The change's API delta — merged into the feature's api-plan.md by finalize-delta.
|
|
3628
|
+
delta_api_plan = (ConvertTo-RepoRelative -Path $api -RepoRoot $repo); delta_api_plan_exists = (Test-Path $api)
|
|
3448
3629
|
wave = $wave
|
|
3449
3630
|
# The lanes this change touches, from its delta-spec's **Lanes** line — every lane
|
|
3450
3631
|
# when it records none (lanes_recorded false).
|
|
@@ -4348,13 +4529,26 @@ function Invoke-CrSnapshot {
|
|
|
4348
4529
|
if (-not (Test-Path -LiteralPath $snap)) { New-Item -ItemType Directory -Path $snap -Force | Out-Null }
|
|
4349
4530
|
$spec = Join-Path $fd 'spec.md'
|
|
4350
4531
|
$model = Join-Path $ctx 'domain-model.md'
|
|
4351
|
-
#
|
|
4352
|
-
#
|
|
4532
|
+
# Every document finalize-delta may rewrite: the two masters it merges into, the API plan
|
|
4533
|
+
# it merges the change's API into, the backend plan the first such merge moves the
|
|
4534
|
+
# endpoints out of, and the API draft it redraws.
|
|
4535
|
+
$backend = Join-Path $fd 'backend-plan.md'
|
|
4536
|
+
if (-not (Test-Path -LiteralPath $backend) -and (Test-Path -LiteralPath (Join-Path $fd 'design.md'))) {
|
|
4537
|
+
$backend = Join-Path $fd 'design.md'
|
|
4538
|
+
}
|
|
4539
|
+
$docs = @(@($spec, 'spec.md'), @($model, 'domain-model.md'),
|
|
4540
|
+
@((Join-Path $fd 'api-plan.md'), 'api-plan.md'),
|
|
4541
|
+
@($backend, (Split-Path -Leaf $backend)),
|
|
4542
|
+
@((Join-Path $fd 'api-draft.json'), 'api-draft.json'))
|
|
4543
|
+
# Flat names, not the source tree's shape: the documents live at different levels, so a
|
|
4544
|
+
# nested copy would encode the layout of the day it was taken.
|
|
4353
4545
|
[string[]]$copied = @()
|
|
4354
|
-
|
|
4546
|
+
$sources = [ordered]@{}
|
|
4547
|
+
foreach ($pair in $docs) {
|
|
4355
4548
|
if (Test-Path -LiteralPath $pair[0] -PathType Leaf) {
|
|
4356
4549
|
Copy-Item -LiteralPath $pair[0] -Destination (Join-Path $snap $pair[1]) -Force
|
|
4357
4550
|
$copied += $pair[1]
|
|
4551
|
+
$sources[$pair[1]] = (ConvertTo-RepoRelative -Path $pair[0] -RepoRoot $repo)
|
|
4358
4552
|
}
|
|
4359
4553
|
}
|
|
4360
4554
|
|
|
@@ -4370,10 +4564,7 @@ function Invoke-CrSnapshot {
|
|
|
4370
4564
|
version = $version
|
|
4371
4565
|
context_version = (Get-MdHeader -Path $model -Name 'Context version')
|
|
4372
4566
|
files = @($copied)
|
|
4373
|
-
sources =
|
|
4374
|
-
'spec.md' = (ConvertTo-RepoRelative -Path $spec -RepoRoot $repo)
|
|
4375
|
-
'domain-model.md' = (ConvertTo-RepoRelative -Path $model -RepoRoot $repo)
|
|
4376
|
-
}
|
|
4567
|
+
sources = $sources
|
|
4377
4568
|
# Stated in the file itself, because the restore is the reader's whole reason for
|
|
4378
4569
|
# opening it and the condition on it is not obvious from the content.
|
|
4379
4570
|
note = ("Masters as they stood immediately before finalize-delta merged $crId. " +
|
|
@@ -4772,6 +4963,31 @@ function Invoke-PlanCoverage {
|
|
|
4772
4963
|
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
|
4773
4964
|
}
|
|
4774
4965
|
|
|
4966
|
+
# Which of a lane plan's requirement ids no test of the slice names. Same core as
|
|
4967
|
+
# plan-coverage (python/aw-plan-coverage.py --citations), called identically by both
|
|
4968
|
+
# shells — see the bash twin.
|
|
4969
|
+
function Invoke-TestCitations {
|
|
4970
|
+
param([string[]]$Rest)
|
|
4971
|
+
$py = Get-Py
|
|
4972
|
+
if (-not $py) { Write-Error 'test-citations: python3 is required'; exit 1 }
|
|
4973
|
+
$core = Join-Path $PSScriptRoot '..' 'python' 'aw-plan-coverage.py'
|
|
4974
|
+
if (-not (Test-Path $core)) { Write-Error "test-citations: core not found at $core"; exit 1 }
|
|
4975
|
+
# --flag=value: a lane with no test path or a feature with no model passes an empty
|
|
4976
|
+
# value, and PowerShell 5.1 drops an empty argument.
|
|
4977
|
+
$fwd = @()
|
|
4978
|
+
for ($i = 0; $i -lt $Rest.Count; $i++) {
|
|
4979
|
+
$a = "$($Rest[$i])"
|
|
4980
|
+
if ($a -in '--plan','--kind','--mode','--paths','--source-path','--test-path','--spec','--model','--known-gaps') {
|
|
4981
|
+
$v = ''
|
|
4982
|
+
if ($i + 1 -lt $Rest.Count -and -not "$($Rest[$i + 1])".StartsWith('--')) { $v = $Rest[++$i] }
|
|
4983
|
+
$fwd += ($a + '=' + $v)
|
|
4984
|
+
} else { $fwd += $a }
|
|
4985
|
+
}
|
|
4986
|
+
$repo = Get-RepoRoot
|
|
4987
|
+
& $py $core '--repo' $repo '--citations' @fwd
|
|
4988
|
+
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
|
4989
|
+
}
|
|
4990
|
+
|
|
4775
4991
|
# Stamp a version into the MASTER documents' `**Version**:` headers — the
|
|
4776
4992
|
# deterministic twin of cr-archive's active_version.json bump. close-delta calls
|
|
4777
4993
|
# this with the version cr-archive just computed, so all THREE carriers of the
|
|
@@ -4805,8 +5021,9 @@ function Invoke-VersionStamp {
|
|
|
4805
5021
|
# spine cannot tolerate. A lane the feature does not have reports `missing` and nothing
|
|
4806
5022
|
# fails, which is also how the legacy names are carried — a feature not yet migrated to
|
|
4807
5023
|
# the plan names still gets stamped, and one that has been migrated reports the legacy
|
|
4808
|
-
# pair missing.
|
|
4809
|
-
foreach ($name in @('spec.md', 'backend-plan.md', 'frontend-plan.md', 'design.md', 'design.ui.md'
|
|
5024
|
+
# pair missing. The API plan is a master too, and moves with them.
|
|
5025
|
+
foreach ($name in @('spec.md', 'backend-plan.md', 'frontend-plan.md', 'design.md', 'design.ui.md',
|
|
5026
|
+
'api-plan.md')) {
|
|
4810
5027
|
$p = Join-Path $fd $name
|
|
4811
5028
|
if (-not (Test-Path $p)) { $missing += $name; continue }
|
|
4812
5029
|
$src = Get-Content -Raw -Encoding UTF8 $p
|
|
@@ -4823,11 +5040,14 @@ function Invoke-VersionStamp {
|
|
|
4823
5040
|
# found by its CR cell, not by position: rows are appended newest-last and a feature may
|
|
4824
5041
|
# carry many, so "the last row" would be right only until two changes were open at once.
|
|
4825
5042
|
# Only a cell still reading '(pending)' is written — a row already carrying a version
|
|
4826
|
-
# belongs to a change that closed, and re-stamping it would rewrite history.
|
|
5043
|
+
# belongs to a change that closed, and re-stamping it would rewrite history. The API
|
|
5044
|
+
# plan's row, which finalize-delta's API merge wrote, is completed the same way.
|
|
4827
5045
|
$rowFilled = $false; $rowWas = ''
|
|
5046
|
+
[string[]]$rowsFilled = @()
|
|
4828
5047
|
if ($crId) {
|
|
4829
|
-
$
|
|
4830
|
-
|
|
5048
|
+
foreach ($name in @('spec.md', 'api-plan.md')) {
|
|
5049
|
+
$p = Join-Path $fd $name
|
|
5050
|
+
if (-not (Test-Path -LiteralPath $p)) { continue }
|
|
4831
5051
|
$lines = [System.IO.File]::ReadAllLines($p)
|
|
4832
5052
|
$changed = $false
|
|
4833
5053
|
for ($i = 0; $i -lt $lines.Count; $i++) {
|
|
@@ -4837,7 +5057,7 @@ function Invoke-VersionStamp {
|
|
|
4837
5057
|
$cells = $line -split '\|'
|
|
4838
5058
|
if ($cells.Count -gt 2 -and
|
|
4839
5059
|
@('(pending)','pending','—','-','') -contains $cells[1].Trim().ToLower()) {
|
|
4840
|
-
$rowWas = $cells[1].Trim()
|
|
5060
|
+
if ($name -eq 'spec.md') { $rowWas = $cells[1].Trim() }
|
|
4841
5061
|
$cells[1] = " $ver "
|
|
4842
5062
|
$lines[$i] = ($cells -join '|')
|
|
4843
5063
|
$changed = $true
|
|
@@ -4846,7 +5066,8 @@ function Invoke-VersionStamp {
|
|
|
4846
5066
|
}
|
|
4847
5067
|
if ($changed) {
|
|
4848
5068
|
[System.IO.File]::WriteAllLines($p, $lines)
|
|
4849
|
-
$
|
|
5069
|
+
$rowsFilled += $name
|
|
5070
|
+
if ($name -eq 'spec.md') { $rowFilled = $true }
|
|
4850
5071
|
}
|
|
4851
5072
|
}
|
|
4852
5073
|
}
|
|
@@ -4856,7 +5077,10 @@ function Invoke-VersionStamp {
|
|
|
4856
5077
|
# Whether the merge's pending row was completed. False with a
|
|
4857
5078
|
# --cr-id is the ordinary outcome for a re-run (already filled)
|
|
4858
5079
|
# and for a merge predating the pending convention.
|
|
4859
|
-
cr_id = $crId; history_row_filled = $rowFilled; history_row_was = $rowWas
|
|
5080
|
+
cr_id = $crId; history_row_filled = $rowFilled; history_row_was = $rowWas
|
|
5081
|
+
# Every document whose pending row was completed: spec.md, and the
|
|
5082
|
+
# API plan when the change merged an API delta into it.
|
|
5083
|
+
history_rows_filled = @($rowsFilled) })
|
|
4860
5084
|
}
|
|
4861
5085
|
|
|
4862
5086
|
# ─────────────────────────────── main ──────────────────────────────────────
|
|
@@ -4874,6 +5098,7 @@ switch ($sub) {
|
|
|
4874
5098
|
'set-domain' { Invoke-SetDomain -Rest $rest }
|
|
4875
5099
|
'stack-list' { Invoke-StackList -Rest $rest }
|
|
4876
5100
|
'stack-detect' { Invoke-StackDetect -Rest $rest }
|
|
5101
|
+
'stack-rules' { Invoke-StackRules -Rest $rest }
|
|
4877
5102
|
'lane-declare' { Invoke-LaneDeclare -Rest $rest }
|
|
4878
5103
|
'lane-resolve' { Invoke-LaneResolve -Rest $rest }
|
|
4879
5104
|
'lane-verify' { Invoke-LaneVerify -Rest $rest }
|
|
@@ -4887,6 +5112,9 @@ switch ($sub) {
|
|
|
4887
5112
|
'contract-client' { Invoke-ContractClient -Rest $rest }
|
|
4888
5113
|
'contract-check' { Invoke-ContractCheck -Rest $rest }
|
|
4889
5114
|
'contract-impact' { Invoke-ContractImpact -Rest $rest }
|
|
5115
|
+
'contract-verify' { Invoke-ContractVerify -Rest $rest }
|
|
5116
|
+
'contract-merge' { Invoke-ContractMerge -Rest $rest }
|
|
5117
|
+
'api-split' { Invoke-ApiSplit -Rest $rest }
|
|
4890
5118
|
'list-contexts' { Invoke-ListContexts -Rest $rest }
|
|
4891
5119
|
'set-context' { Invoke-SetContext -Rest $rest }
|
|
4892
5120
|
'list-features' { Invoke-ListFeatures -Rest $rest }
|
|
@@ -4923,11 +5151,14 @@ switch ($sub) {
|
|
|
4923
5151
|
'context-version-bump' { Invoke-ContextVersionBump -Rest $rest }
|
|
4924
5152
|
'context-paths' { Invoke-ContextPaths -Rest $rest }
|
|
4925
5153
|
'model-scaffold' { Invoke-ModelScaffold -Rest $rest }
|
|
5154
|
+
'model-guard-snapshot' { Invoke-ModelGuard -Op 'snapshot' -Rest $rest }
|
|
5155
|
+
'model-guard-check' { Invoke-ModelGuard -Op 'check' -Rest $rest }
|
|
4926
5156
|
'version-stamp' { Invoke-VersionStamp -Rest $rest }
|
|
4927
5157
|
'plan-gaps' { Invoke-PlanGaps -Rest $rest }
|
|
4928
5158
|
'plan-gate-stamp' { Invoke-PlanGateStamp -Rest $rest }
|
|
4929
5159
|
'plan-gate-check' { Invoke-PlanGateCheck -Rest $rest }
|
|
4930
5160
|
'plan-coverage' { Invoke-PlanCoverage -Rest $rest }
|
|
5161
|
+
'test-citations' { Invoke-TestCitations -Rest $rest }
|
|
4931
5162
|
'gap-route' { Invoke-GapRoute -Rest $rest }
|
|
4932
5163
|
'checklist-read' { Invoke-ChecklistRead -Rest $rest }
|
|
4933
5164
|
'checklist-apply' { Invoke-ChecklistApply -Rest $rest }
|