@caddis/cli 0.4.18 → 0.4.20

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 (43) hide show
  1. package/bundles/antigravity-plugin/plugin.json +1 -1
  2. package/bundles/antigravity-plugin/scripts/caddis_adhoc.py +530 -0
  3. package/bundles/antigravity-plugin/scripts/caddis_lanes.py +8 -0
  4. package/bundles/antigravity-plugin/skills/_registry.md +5 -0
  5. package/bundles/antigravity-plugin/skills/data-lineage/SKILL.md +32 -0
  6. package/bundles/antigravity-plugin/skills/hld/SKILL.md +39 -0
  7. package/bundles/antigravity-plugin/skills/lld/SKILL.md +45 -0
  8. package/bundles/antigravity-plugin/skills/mmr/SKILL.md +87 -0
  9. package/bundles/antigravity-plugin/skills/port-to-github/SKILL.md +231 -0
  10. package/bundles/antigravity-plugin/skills/port-to-github/references/ci-and-credentials.md +66 -0
  11. package/bundles/antigravity-plugin/skills/port-to-github/references/config.example.json +18 -0
  12. package/bundles/antigravity-plugin/skills/port-to-github/references/old-host.md +61 -0
  13. package/bundles/antigravity-plugin/skills/port-to-github/scripts/Compare-PortRefs.ps1 +68 -0
  14. package/bundles/antigravity-plugin/skills/port-to-github/scripts/Convert-PortWorkflow.ps1 +92 -0
  15. package/bundles/antigravity-plugin/skills/port-to-github/scripts/Get-PortSource.ps1 +459 -0
  16. package/bundles/antigravity-plugin/skills/port-to-github/scripts/PortCommon.ps1 +130 -0
  17. package/bundles/antigravity-plugin/skills/port-to-github/scripts/Publish-PortToGitHub.ps1 +156 -0
  18. package/bundles/antigravity-plugin/skills/port-to-github/scripts/Switch-CheckoutOrigin.ps1 +182 -0
  19. package/bundles/antigravity-plugin/skills/port-to-github/scripts/Test-PortReadiness.ps1 +218 -0
  20. package/bundles/antigravity-plugin/skills/port-to-github/scripts/Test-PortWorkflowParses.ps1 +62 -0
  21. package/bundles/antigravity-plugin-extras/plugin.json +1 -1
  22. package/bundles/antigravity-plugin-extras/skills/_registry.md +3 -0
  23. package/bundles/antigravity-plugin-extras/skills/data-lineage/SKILL.md +158 -0
  24. package/bundles/antigravity-plugin-extras/skills/data-lineage/scripts/convert_doc_to_confluence.py +1430 -0
  25. package/bundles/antigravity-plugin-extras/skills/hld/SKILL.md +220 -0
  26. package/bundles/antigravity-plugin-extras/skills/hld/references/gold-standard-hld.md +428 -0
  27. package/bundles/antigravity-plugin-extras/skills/hld/scripts/convert_doc_to_confluence.py +1430 -0
  28. package/bundles/antigravity-plugin-extras/skills/lld/SKILL.md +198 -0
  29. package/bundles/antigravity-plugin-extras/skills/lld/scripts/convert_doc_to_confluence.py +1430 -0
  30. package/bundles/codex/.codex/skills/devops/port-to-github/SKILL.md +231 -0
  31. package/bundles/codex/.codex/skills/devops/port-to-github/references/ci-and-credentials.md +66 -0
  32. package/bundles/codex/.codex/skills/devops/port-to-github/references/config.example.json +18 -0
  33. package/bundles/codex/.codex/skills/devops/port-to-github/references/old-host.md +61 -0
  34. package/bundles/codex/.codex/skills/devops/port-to-github/scripts/Compare-PortRefs.ps1 +68 -0
  35. package/bundles/codex/.codex/skills/devops/port-to-github/scripts/Convert-PortWorkflow.ps1 +92 -0
  36. package/bundles/codex/.codex/skills/devops/port-to-github/scripts/Get-PortSource.ps1 +459 -0
  37. package/bundles/codex/.codex/skills/devops/port-to-github/scripts/PortCommon.ps1 +130 -0
  38. package/bundles/codex/.codex/skills/devops/port-to-github/scripts/Publish-PortToGitHub.ps1 +156 -0
  39. package/bundles/codex/.codex/skills/devops/port-to-github/scripts/Switch-CheckoutOrigin.ps1 +182 -0
  40. package/bundles/codex/.codex/skills/devops/port-to-github/scripts/Test-PortReadiness.ps1 +218 -0
  41. package/bundles/codex/.codex/skills/devops/port-to-github/scripts/Test-PortWorkflowParses.ps1 +62 -0
  42. package/bundles/manifest.json +3 -3
  43. package/package.json +1 -1
@@ -0,0 +1,218 @@
1
+ <#
2
+ .SYNOPSIS
3
+ Inspect a staged repository and list what will break or leak when it moves to GitHub.
4
+
5
+ .DESCRIPTION
6
+ Reads <staging>\<Name>\mirror.git (written by Get-PortSource.ps1). Changes nothing.
7
+ Writes <staging>\<Name>\readiness.json and prints a report.
8
+
9
+ BLOCKERS (exit 1): a file over 100 MB anywhere in history, Git LFS content, a workflow
10
+ containing an empty expression, a likely live credential in the current tree.
11
+ WARNINGS: everything a human must decide: CI credentials, dependency hosts, runner labels,
12
+ hardcoded references to the old host, secret-looking files in past history.
13
+ #>
14
+ [CmdletBinding()]
15
+ param(
16
+ [Parameter(Mandatory)][string]$Name,
17
+ [string]$StagingRoot,
18
+ [string]$ConfigPath
19
+ )
20
+
21
+ $ErrorActionPreference = 'Stop'
22
+ . (Join-Path $PSScriptRoot 'PortCommon.ps1')
23
+
24
+ $config = Get-PortConfig $ConfigPath
25
+ $stage = Join-Path (Get-PortStagingRoot $config $StagingRoot) $Name
26
+ $mirror = Join-Path $stage 'mirror.git'
27
+ if (-not (Test-Path -LiteralPath $mirror)) { throw "No staged mirror at $mirror. Run Get-PortSource.ps1 first." }
28
+ $manifest = Get-Content -LiteralPath (Join-Path $stage 'manifest.json') -Raw | ConvertFrom-Json
29
+ $branch = $manifest.defaultBranch
30
+
31
+ $blockers = New-Object System.Collections.Generic.List[string]
32
+ $warnings = New-Object System.Collections.Generic.List[string]
33
+ $report = [ordered]@{ name = $Name; defaultBranch = $branch; checkedAt = (Get-Date).ToString('o') }
34
+
35
+ function MGit([string[]]$a) { Invoke-PortGit (@('-C', $mirror) + $a) }
36
+ function MGitTry([string[]]$a) { Invoke-PortGit -AllowFailure (@('-C', $mirror) + $a) }
37
+
38
+ # ---- 1. Size: GitHub refuses any blob over 100 MB, in ANY commit, not just the tip ----------
39
+ Write-PortStep 'Checking object sizes across all history'
40
+ $big = @()
41
+ foreach ($line in (MGit @('cat-file', '--batch-all-objects', '--batch-check=%(objecttype) %(objectsize) %(objectname)'))) {
42
+ $t, $s, $o = $line -split ' '
43
+ if ($t -eq 'blob' -and [int64]$s -gt 50MB) { $big += [pscustomobject]@{ sha = $o; bytes = [int64]$s } }
44
+ }
45
+ if ($big.Count -gt 0) {
46
+ $paths = @{}
47
+ foreach ($l in (MGit @('rev-list', '--objects', '--all'))) {
48
+ $sha, $p = $l -split ' ', 2
49
+ if ($p -and ($big.sha -contains $sha)) { $paths[$sha] = $p }
50
+ }
51
+ foreach ($b in $big) {
52
+ $p = $paths[$b.sha]
53
+ $mb = [math]::Round($b.bytes / 1MB, 1)
54
+ if ($b.bytes -gt 100MB) { $blockers.Add("File over 100 MB in history: $p ($mb MB). GitHub rejects the push. Rewrite history (git filter-repo) or move it to LFS/releases first.") }
55
+ else { $warnings.Add("Large file in history: $p ($mb MB). GitHub warns above 50 MB.") }
56
+ }
57
+ }
58
+ $report.largeBlobs = $big.Count
59
+
60
+ # ---- 2. LFS ---------------------------------------------------------------------------------
61
+ $attrs = MGitTry @('show', "${branch}:.gitattributes")
62
+ if ($attrs.ExitCode -eq 0 -and (($attrs.Output -join "`n") -match 'filter=lfs')) {
63
+ $blockers.Add('The repository uses Git LFS. LFS objects are not in a git mirror; push them separately (git lfs fetch --all from the source, git lfs push --all to GitHub) before declaring the port done.')
64
+ }
65
+
66
+ # ---- 3. Secrets ---------------------------------------------------------------------------------
67
+ Write-PortStep 'Scanning for credentials'
68
+ $liveSecretPatterns = @('gh[pousr]_[A-Za-z0-9]{36,}', 'github_pat_[A-Za-z0-9_]{40,}', 'AKIA[0-9A-Z]{16}', '-----BEGIN [A-Z ]*PRIVATE KEY-----', 'xox[baprs]-[A-Za-z0-9-]{10,}')
69
+ $grepArgs = @('grep', '-I', '-l', '-E')
70
+ $liveHits = @()
71
+ foreach ($p in $liveSecretPatterns) {
72
+ $g = MGitTry ($grepArgs + @('-e', $p, $branch))
73
+ if ($g.ExitCode -eq 0) { $liveHits += $g.Output }
74
+ }
75
+ foreach ($h in ($liveHits | Sort-Object -Unique)) { $blockers.Add("Likely live credential in the current tree: $h. Remove it and ROTATE it; history still holds it.") }
76
+
77
+ # The same patterns across ALL history. A token committed once and deleted later is gone from
78
+ # the tip but still readable by anyone who can clone the new repo. -G matches commits whose
79
+ # diff adds or removes a matching line (extended regex).
80
+ $histPattern = $liveSecretPatterns -join '|'
81
+ $histOut = @(MGit @('log', '--all', "-G$histPattern", '--format=@@%h', '--name-only'))
82
+ $cur = $null
83
+ $histHits = @{}
84
+ foreach ($l in $histOut) {
85
+ if (-not $l) { continue }
86
+ if ($l.StartsWith('@@')) { $cur = $l.Substring(2); continue }
87
+ if (-not $histHits.ContainsKey($l)) { $histHits[$l] = @() }
88
+ $histHits[$l] += $cur
89
+ }
90
+ foreach ($file in ($histHits.Keys | Sort-Object)) {
91
+ $tipName = "${branch}:$file"
92
+ if ($liveHits -contains $tipName) { continue } # already reported as a current-tree blocker
93
+ $blockers.Add("Likely credential in PAST history: $file (commit(s) $(($histHits[$file] | Select-Object -Unique) -join ', ')). It is gone from the tip but ships with the history. ROTATE it, then either accept that or rewrite history (git filter-repo) before publishing.")
94
+ }
95
+ $report.historySecretFiles = @($histHits.Keys)
96
+
97
+ $pwHits = MGitTry @('grep', '-I', '-l', '-i', '-E', '-e', '(password|passwd|pwd|api[_-]?key|secret)[[:space:]]*[:=][[:space:]]*[''"][^''" ]{8,}[''"]', $branch)
98
+ if ($pwHits.ExitCode -eq 0) {
99
+ foreach ($h in $pwHits.Output) { $warnings.Add("Possible hardcoded password or key: $h. Check it is a placeholder or test value.") }
100
+ }
101
+
102
+ $treeFiles = @(MGit @('ls-tree', '-r', '--name-only', $branch))
103
+ foreach ($f in $treeFiles) {
104
+ if (Test-PortSecretName (Split-Path $f -Leaf)) { $warnings.Add("Secret-type file tracked in the current tree: $f. Check it holds no real value.") }
105
+ }
106
+ $histNames = @(MGit @('log', '--all', '--format=', '--name-only') | Where-Object { $_ } | Sort-Object -Unique)
107
+ $histSecret = @($histNames | Where-Object { (Test-PortSecretName (Split-Path $_ -Leaf)) -and ($treeFiles -notcontains $_) })
108
+ foreach ($f in $histSecret) { $warnings.Add("Secret-type file existed in PAST history: $f. It will be public to anyone who can read the new repo. Rotate what it held, or rewrite history first.") }
109
+
110
+ # ---- 4. CI ----------------------------------------------------------------------------------
111
+ Write-PortStep 'Inventorying CI'
112
+ $ciFiles = @($treeFiles | Where-Object {
113
+ $_ -match '^\.(gitea|github|forgejo)/workflows/[^/]+\.ya?ml$' -or
114
+ $_ -in @('.gitlab-ci.yml', 'Jenkinsfile', 'azure-pipelines.yml', 'bitbucket-pipelines.yml', '.drone.yml', '.woodpecker.yml')
115
+ })
116
+ $report.ciFiles = $ciFiles
117
+ $workflows = @()
118
+ foreach ($f in $ciFiles) {
119
+ $text = (MGit @('show', "${branch}:$f")) -join "`n"
120
+ $lines = $text -split "`n"
121
+ $wf = [ordered]@{ file = $f; emptyExpressions = @(); credentialLines = @(); runsOn = @(); hosts = @(); dispatchOnly = $false }
122
+ for ($i = 0; $i -lt $lines.Count; $i++) {
123
+ $ln = $lines[$i]
124
+ if ($ln -match '\$\{\{\s*\}\}') { $wf.emptyExpressions += ($i + 1) }
125
+ if ($ln -match 'secrets\.|github\.token|(?i)token\s*:|_TOKEN\b|_USER\b|PASSWORD') { $wf.credentialLines += ("{0}: {1}" -f ($i + 1), $ln.Trim()) }
126
+ if ($ln -match '^\s*runs-on:\s*(.+)$') { $wf.runsOn += $Matches[1].Trim() }
127
+ foreach ($m in [regex]::Matches($ln, 'https?://([A-Za-z0-9.-]+(:\d+)?)')) { $wf.hosts += $m.Groups[1].Value }
128
+ }
129
+ $wf.runsOn = @($wf.runsOn | Sort-Object -Unique)
130
+ $wf.hosts = @($wf.hosts | Where-Object { $_ -notmatch '^(github\.com|api\.github\.com|localhost|127\.0\.0\.1)' } | Sort-Object -Unique)
131
+ if ($text -match '(?m)^on:\s*(\[\s*)?workflow_dispatch' -and $text -notmatch '(?m)^\s+(push|pull_request)\s*:') { $wf.dispatchOnly = $true }
132
+ if ($wf.emptyExpressions.Count -gt 0) {
133
+ $msg = "$f has an empty `${{ }} on line(s) $($wf.emptyExpressions -join ', '). GitHub rejects the WHOLE file (startup_failure, zero jobs), even inside a comment in a run: block."
134
+ # GitHub parses only .github/workflows. A .gitea/.forgejo file is inert on GitHub until
135
+ # phase 4 copies it, so it is a warning here and a blocker there.
136
+ if ($f -like '.github/*') { $blockers.Add($msg) } else { $warnings.Add("$msg Fix it when translating (phase 4).") }
137
+ }
138
+ if ($wf.dispatchOnly) { $warnings.Add("$f triggers only on workflow_dispatch. If this is a FROZEN copy, generate the GitHub workflow from the last live version in history instead.") }
139
+ if ($wf.hosts.Count -gt 0) { $warnings.Add("$f references non-GitHub host(s): $($wf.hosts -join ', '). Each needs a credential that works FROM GitHub's runners, or the step breaks.") }
140
+ $workflows += [pscustomobject]$wf
141
+ }
142
+ $report.workflows = $workflows
143
+ # Workflows already under .github/workflows RUN on GitHub the moment their branch is pushed.
144
+ # Check every branch, not only the default one: a push of any branch fires its own workflows.
145
+ $liveOnPush = @()
146
+ foreach ($h in @(MGit @('for-each-ref', '--format=%(refname:short)', 'refs/heads'))) {
147
+ $ls = MGitTry @('ls-tree', '--name-only', "${h}:.github/workflows")
148
+ if ($ls.ExitCode -eq 0 -and ($ls.Output -join '')) { $liveOnPush += $h }
149
+ }
150
+ $report.branchesWithGithubWorkflows = $liveOnPush
151
+ if ($liveOnPush.Count -gt 0) {
152
+ $warnings.Add("$($liveOnPush.Count) branch(es) already contain .github/workflows ($($liveOnPush -join ', ')). Publishing PUSHES them, which fires every push-triggered workflow - including any deploy job. Publish refuses unless you pass -AllowWorkflowRuns after reading those workflows.")
153
+ }
154
+
155
+ $otherCi = @($ciFiles | Where-Object { $_ -notmatch '^\.(gitea|github|forgejo)/workflows/' })
156
+ if ($otherCi.Count -gt 0) {
157
+ $warnings.Add("Non-Actions CI found ($($otherCi -join ', ')). It will not run on GitHub; it must be rewritten as a GitHub Actions workflow.")
158
+ }
159
+ if ($ciFiles.Count -eq 0) { $warnings.Add('No CI found. After the port nothing builds, tests or deploys this app automatically.') }
160
+
161
+ # ---- 5. Dependencies that live on another host --------------------------------------------------
162
+ Write-PortStep 'Checking dependency pins'
163
+ $depHosts = @()
164
+ foreach ($f in ($treeFiles | Where-Object { $_ -match '(^|/)(pyproject\.toml|requirements[^/]*\.txt|setup\.cfg|package\.json|\.npmrc|\.pypirc|pip\.conf|go\.mod|Cargo\.toml)$' })) {
165
+ $text = (MGit @('show', "${branch}:$f")) -join "`n"
166
+ foreach ($m in [regex]::Matches($text, '(git\+)?(https?|ssh)://(?:[^@/\s]+@)?([A-Za-z0-9.-]+(:\d+)?)/')) {
167
+ $h = $m.Groups[3].Value
168
+ if ($h -match '^(pypi\.org|files\.pythonhosted\.org|registry\.npmjs\.org|github\.com)$') {
169
+ if ($h -eq 'github.com' -and $m.Groups[1].Value) { $depHosts += [pscustomobject]@{ file = $f; host = $h } }
170
+ continue
171
+ }
172
+ $depHosts += [pscustomobject]@{ file = $f; host = $h }
173
+ }
174
+ if ($text -match '(?m)^\s*registry\s*=') { $warnings.Add("$f sets a custom package registry. CI on GitHub needs a credential for it, or the install fails.") }
175
+ }
176
+ $report.dependencyHosts = $depHosts
177
+ foreach ($h in ($depHosts | Group-Object host)) {
178
+ $warnings.Add("Dependencies pinned from $($h.Name) in: $((($h.Group.file) | Sort-Object -Unique) -join ', '). CI and every deploy host need a read credential for THAT host, chosen per host.")
179
+ }
180
+
181
+ # ---- 6. References to the old host --------------------------------------------------------------
182
+ $oldHost = $null
183
+ if ($manifest.sourceKind -eq 'url') {
184
+ if ($manifest.source -match '^[a-z]+://(?:[^@/]+@)?(?<h>[^/:]+)') { $oldHost = $Matches.h }
185
+ elseif ($manifest.source -match '^[^@\s]+@(?<h>[^:\s]+):') { $oldHost = $Matches.h } # scp form: git@host:org/repo.git
186
+ }
187
+ if ($oldHost) {
188
+ $ref = MGitTry @('grep', '-I', '-l', '-F', '-e', $oldHost, $branch)
189
+ if ($ref.ExitCode -eq 0) {
190
+ $files = @($ref.Output | ForEach-Object { $_ -replace "^${branch}:", '' })
191
+ $report.oldHostReferences = $files
192
+ $warnings.Add("$($files.Count) file(s) still name the old host $oldHost ($($files[0..([math]::Min(4, $files.Count - 1))] -join ', ')$(if ($files.Count -gt 5) { ', ...' })). Decide per file: a dependency pin (keep, needs a credential) or an origin reference (change).")
193
+ }
194
+ }
195
+
196
+ # ---- 7. Carry forward what the source step found -----------------------------------------------
197
+ foreach ($w in $manifest.warnings) { $warnings.Add($w) }
198
+
199
+ $report.blockers = @($blockers)
200
+ $report.warnings = @($warnings)
201
+ $report.ready = ($blockers.Count -eq 0)
202
+ ConvertTo-PortJsonFile $report (Join-Path $stage 'readiness.json')
203
+
204
+ Write-Host ''
205
+ Write-Host "Readiness for $Name (branch $branch): $($blockers.Count) blocker(s), $($warnings.Count) warning(s)"
206
+ foreach ($b in $blockers) { Write-PortFail $b }
207
+ foreach ($w in $warnings) { Write-PortWarn $w }
208
+ foreach ($wf in $workflows) {
209
+ if ($wf.credentialLines.Count -gt 0) {
210
+ Write-Host ''
211
+ Write-Host "Credential lines in $($wf.file) - classify EACH as ORIGIN (becomes github.token) or DEPENDENCY (keeps a token for that host):"
212
+ $wf.credentialLines | ForEach-Object { Write-Host " $_" }
213
+ }
214
+ if ($wf.runsOn.Count -gt 0) { Write-Host "Runner labels in $($wf.file): $($wf.runsOn -join ' | ') - confirm GitHub has runners with these labels." }
215
+ }
216
+ Write-Host " report: $(Join-Path $stage 'readiness.json')"
217
+ if ($blockers.Count -gt 0) { exit 1 }
218
+ exit 0
@@ -0,0 +1,62 @@
1
+ <#
2
+ .SYNOPSIS
3
+ After pushing a feature branch, prove GitHub could PARSE its workflows.
4
+
5
+ .DESCRIPTION
6
+ GitHub cannot evaluate the triggers of a workflow file it cannot parse, so an invalid file
7
+ produces a failed run with ZERO jobs ("startup_failure") on every push, even on a branch its
8
+ triggers would never match. A valid file whose triggers do not match produces no run at all.
9
+
10
+ So, for the pushed commit:
11
+ a run with conclusion startup_failure, or failure with zero jobs -> INVALID (exit 1)
12
+ no run, or runs with real jobs -> parsed (exit 0)
13
+
14
+ Waits -WaitSeconds for GitHub to react, because an invalid file's run appears within seconds
15
+ and "no run" is only meaningful after that window.
16
+ #>
17
+ [CmdletBinding()]
18
+ param(
19
+ [Parameter(Mandatory)][string]$Owner,
20
+ [Parameter(Mandatory)][string]$Repo,
21
+ [Parameter(Mandatory)][string]$Branch,
22
+ [string]$Sha,
23
+ [int]$WaitSeconds = 60
24
+ )
25
+
26
+ $ErrorActionPreference = 'Stop'
27
+ . (Join-Path $PSScriptRoot 'PortCommon.ps1')
28
+ $full = "$Owner/$Repo"
29
+
30
+ if (-not $Sha) {
31
+ $Sha = (& gh api "repos/$full/commits/$Branch" --jq .sha 2>&1)
32
+ if ($LASTEXITCODE -ne 0) { throw "Branch $Branch not found on ${full}: $Sha" }
33
+ }
34
+ Write-PortStep "Watching $full @ $($Sha.Substring(0,12)) on $Branch for $WaitSeconds s"
35
+
36
+ $deadline = (Get-Date).AddSeconds($WaitSeconds)
37
+ $invalid = @()
38
+ $runs = @()
39
+ do {
40
+ Start-Sleep -Seconds 10
41
+ $json = & gh api "repos/$full/actions/runs?head_sha=$Sha&per_page=50" 2>&1
42
+ if ($LASTEXITCODE -ne 0) { throw "Cannot list runs: $json" }
43
+ $runs = @(($json | ConvertFrom-Json).workflow_runs)
44
+ $invalid = @()
45
+ foreach ($r in $runs) {
46
+ if ($r.conclusion -eq 'startup_failure') { $invalid += $r; continue }
47
+ if ($r.status -eq 'completed' -and $r.conclusion -eq 'failure') {
48
+ $jobs = (& gh api "repos/$full/actions/runs/$($r.id)/jobs" --jq .total_count 2>$null)
49
+ if ("$jobs" -eq '0') { $invalid += $r }
50
+ }
51
+ }
52
+ if ($invalid.Count -gt 0) { break }
53
+ } while ((Get-Date) -lt $deadline)
54
+
55
+ if ($invalid.Count -gt 0) {
56
+ foreach ($r in $invalid) { Write-PortFail "INVALID workflow: '$($r.name)' ($($r.path)) - $($r.html_url)" }
57
+ Write-Host ' GitHub gives no line number. Look first for an empty ${{ }}; then bisect by removing jobs from the END (removing a middle job breaks needs: and fails the same way).'
58
+ exit 1
59
+ }
60
+ if ($runs.Count -eq 0) { Write-PortOk "No run for this commit: every workflow parsed and none matched a branch push. This is the expected result for main-only triggers." }
61
+ else { foreach ($r in $runs) { Write-PortOk "Run '$($r.name)' has real jobs ($($r.status)/$($r.conclusion)): the file parsed. $($r.html_url)" } }
62
+ exit 0
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "caddis-extras",
3
- "version": "1.3.29",
3
+ "version": "1.3.31",
4
4
  "description": "caddis-extras — the long-tail skills for the Antigravity (agy) caddis plugin; install alongside caddis for the full set."
5
5
  }
@@ -59,8 +59,11 @@
59
59
  | Skill | Path | When to Use |
60
60
  |-------|------|-------------|
61
61
  | Architecture Document | `architecture-document/` | Generate professional enterprise-grade HLD and LLD documents from the living Architecture.md. Outputs DOCX (editable) or PPTX (executive summary) using existing document skills. |
62
+ | Data Lineage | `data-lineage/` | Generates End-to-End Data Lineage and Telemetry Specifications linking UI pages, API routes, database datasets, and ETL/SP producers for watch-sight observability. |
62
63
  | Doc Coauthoring | `doc-coauthoring/` | Guide users through a structured workflow for co-authoring documentation. Use when user wants to write documentation, proposals, technical specs, decision docs, or similar structured content. This workflow helps users efficiently transfer context, refine content through iteration, and verify the doc works for readers. Trigger when user mentions writing docs, creating proposals, drafting specs, or similar documentation tasks. |
63
64
  | Documentation Analyzer | `documentation-analyzer/` | Analyze codebases, explain code functionality, and generate comprehensive documentation. Use when documenting a project, creating README files, or understanding complex code. |
65
+ | Hld | `hld/` | Generates High-Level Design (HLD) Markdown documentation and converts to Confluence Storage Format XHTML with native PlantUML diagrams and auto-generated legends. |
66
+ | Lld | `lld/` | Generates Lower-Level Design (LLD) Markdown documentation and converts to Confluence Storage Format XHTML with native PlantUML diagrams and auto-generated legends. |
64
67
  | Naming Analyzer | `naming-analyzer/` | Suggest better variable, function, and class names based on context and conventions. |
65
68
  | Prd To Code | `prd-to-code/` | Transform PRD documents into working application code using a systematic 6-phase methodology. Use when starting projects from requirements or converting specs to implementation. |
66
69
 
@@ -0,0 +1,158 @@
1
+ ---
2
+ name: data-lineage
3
+ description: Generates End-to-End Data Lineage and Telemetry Specifications linking UI pages, API routes, database datasets, and ETL/SP producers for watch-sight observability.
4
+ ---
5
+
6
+ # Data Lineage Skill
7
+
8
+ This skill generates End-to-End Data Lineage and Telemetry Specifications for enterprise applications. It bridges the gap between frontend user surfaces, backend API routes, relational database objects, and upstream data producers (ETLs, Stored Procedures, and SQL Agent jobs).
9
+
10
+ The resulting specifications serve as the ground truth for estate-wide observability in **watch-sight** to enable automated dependency graphs, health probing, and blast-radius impact analysis.
11
+
12
+ ---
13
+
14
+ ## Mandatory Workflow Steps
15
+
16
+ ### Step 1: Codebase & Infrastructure Discovery
17
+ Conduct targeted research across the target application and observability repositories:
18
+ 1. **Frontend Surfaces:** Inspect `frontend/src/routes/` or router configurations to identify all user-facing pages, views, and navigation tabs.
19
+ 2. **API Routes:** Inspect `src/api/routes/` or API routers to map every HTTP endpoint invoked by each frontend page.
20
+ 3. **Database Repositories & SQL Queries:** Trace API controllers into data services and repositories to identify exact SQL tables, views, and columns accessed.
21
+ 4. **Data Producers & Execution Timings (DB Server SPs & ETLs):** Identify the scheduled jobs, batch Python scripts, and SQL Server Stored Procedures that populate, calculate, or refresh each dataset. Discover their exact execution timings, run frequencies, and triggers (e.g. SQL Server Agent schedules, Windows Task Scheduler jobs, cron expressions, email triggers, or polling loops from runbooks and pipeline schedule references).
22
+ 5. **Physical Estate Host Resolution:** Query `E:\Projects\watch-sight\config\monitor-targets.json` and `docs/runbooks/estate-pipelines/` to resolve physical server hostnames, IP addresses, ports, database instances, and service accounts.
23
+
24
+ ### Step 2: Markdown Document Authoring
25
+ Draft the canonical Data Lineage Markdown file:
26
+ `docs/<app>/<app>-data-lineage.md`
27
+
28
+ Follow the standard 5-section lineage structure detailed below.
29
+
30
+ ### Step 3: Confluence XHTML Compilation & Verification
31
+ Compile the Markdown document into Confluence Storage Format XHTML using:
32
+ ```powershell
33
+ python scripts/convert_doc_to_confluence.py docs/<app>/<app>-data-lineage.md --output docs/<app>/<app>-data-lineage.html --diagram-type plantuml
34
+ ```
35
+ Verify that the output compiles as well-formed XML without errors.
36
+
37
+ ---
38
+
39
+ ## Standard Lineage Document Structure (Markdown)
40
+
41
+ Every lineage document must follow this standardized 5-section structure:
42
+
43
+ ```markdown
44
+ ---
45
+ type: lineage
46
+ status: current
47
+ feature: <app>-data-lineage
48
+ creation-agent: caddis
49
+ Creating Model: <active model ID, e.g. gemini-3.8-flash>
50
+ ---
51
+
52
+ # <App Title> — End-to-End Data Lineage & Telemetry Specification
53
+
54
+ ## 1. Physical Estate & Host Infrastructure
55
+ Detail the physical hosts, IP addresses, ports, database instances, and network access paths:
56
+ - Application Host (Web server, reverse proxy, ports)
57
+ - Primary Database Host (SQL Server, database instance, port, auth mechanism)
58
+ - Ingestion & Staging Host (Batch runner, scheduled jobs, agent services)
59
+ - External Feeds & Gateways (SFTP drops, upstream BSS/mediation systems)
60
+
61
+ ## 2. End-to-End Dependency & Impact Flowchart
62
+ Provide a colorful, professional Mermaid diagram (`flowchart TD` or `flowchart LR`) illustrating the 5-tier dependency pipeline:
63
+ Upstream Ingestion -> Processing / DB Stored Procedures -> Database Tables/Views -> API Routes -> Frontend UI Pages.
64
+ - Use explicit classDef pastel styles (never raw yellow boxes).
65
+ - Group layers into clear subgraphs.
66
+
67
+ ## 3. Comprehensive Database Lineage Matrix
68
+ A comprehensive table specifying the complete end-to-end data lineage across the following mandatory columns:
69
+ | App | UI Page | API Route | Dataset (Tables / Views) | Producer (ETL / SP / Job) | Execution Timing / Trigger | Database | Physical Host |
70
+ |---|---|---|---|---|---|---|---|
71
+ | ... | ... | ... | ... | ... | ... | ... | ... |
72
+
73
+ Column specifications:
74
+ - **App:** Application identifier (e.g. `rev-sight`).
75
+ - **UI Page:** User interface page path and title (e.g. `/runs (Run Monitor)`).
76
+ - **API Route:** HTTP method and endpoint (e.g. `GET /api/runs`).
77
+ - **Dataset:** Schema-qualified tables or views read (e.g. `[dbo].[CDR_RUN_LOG]`).
78
+ - **Producer:** Exact SQL Agent job name, batch ETL script, or DB Stored Procedure that populates or refreshes the dataset.
79
+ - **Execution Timing / Trigger:** Exact schedule, frequency, and trigger mechanism (e.g. `Daily 07:00 UTC (SQL Agent)`, `Nightly 08:00 UTC (Task Scheduler)`, `Every 5 min polling (Background Service)`).
80
+ - **Database:** Target database name (e.g. `CDR`, `CERILLION`).
81
+ - **Physical Host:** Hostname and IP (e.g. `iegew3ccdr01 (100.126.185.4)`).
82
+
83
+ ## 4. Stored Procedures & Producer Execution Catalog
84
+ Catalog the database server stored procedures and scheduled jobs that populate the datamart:
85
+ - Sequential execution order reference table detailing every job in execution order, execution time, host machine, scheduler, and dependencies.
86
+ - Detailed procedure profiles: Procedure Name, database instance, trigger mechanism, exact schedule, parameters, source tables read, and target tables written.
87
+
88
+ ## 5. watch-sight Telemetry Ingestion Contract
89
+ Provide a machine-readable JSON specification block aligned with `watch-sight/config/monitor-targets.json` implementing the **Three-Tier Real-Time Schedule Discovery Hierarchy**:
90
+ - **All Producers Represented:** Every single producer (ETL script, SQL Agent job, Stored Procedure) cataloged in Section 4 must be represented in the telemetry contract.
91
+ - **Tier 1 (Live Scheduler Discovery):** Define authoritative real-time queries against OS and DB schedulers (`msdb.dbo.sysschedules` / `msdb.dbo.sysjobschedules` for SQL Agent jobs; PowerShell `Get-ScheduledTaskInfo` for Windows Task Scheduler) so monitoring automatically tracks schedule changes made by operators in real time without documentation drift.
92
+ - **Tier 2 (Execution Audit Telemetry):** Define queries against application execution audit tables where available (`[dbo].[ETL_AUDIT_LOG]`, `[dbo].[CDR_LOAD_LOG]`, `[dbo].[cdr_validation_run]`, `msdb.dbo.sysjobhistory`) for actual runtime watermarks, durations, and status. If an application lacks audit tables, Tier 2 is omitted and Tier 1 scheduler status is used.
93
+ - **Tier 3 (Static Baseline Fallback):** Define static baseline fallback schedules and maximum allowable runtimes as a safety guardrail when live probes are momentarily unreachable.
94
+ - `hosts` & `db_ping_targets`
95
+ - `pipeline_producers` (with Tier 1, Tier 2, and Tier 3 blocks)
96
+ - `continuous_services`
97
+ - `application_components`
98
+ - `blast_radius_rules`
99
+ ```
100
+
101
+ ---
102
+
103
+ ## Diagram and Conversion Standards (Expert Systems Architect & Mermaid Designer)
104
+
105
+ ### 🎨 Design & Styling Principles
106
+ 1. **Color Palette & Semantic Styling (Standard/Impact/Existing):**
107
+ - Use desaturated, modern executive colors. Do not use highly saturated primary colors.
108
+ - Define exactly 3-4 standard semantic class styles at the top of every diagram:
109
+ - `existingStyle`: Clean Blue (`fill:#EFF6FF,stroke:#2563EB,stroke-width:2px,color:#1E40AF`) for legacy/established components.
110
+ - `newStyle`: Warm Red/Crimson (`fill:#FEF2F2,stroke:#DC2626,stroke-width:2px,color:#991B1B`) for greenfield/new components.
111
+ - `impactedStyle`: Amber/Gold (`fill:#FFFBEB,stroke:#D97706,stroke-width:2px,color:#92400E`) for modified/impacted systems.
112
+ - Apply classes consistently to the nodes using the triple colon notation (e.g., `NodeName:::newStyle`).
113
+ 2. **Diagram Scalability Settings (Initialization Block):**
114
+ - Always prepend diagrams with a configuration initialization block to control sizing and font scales:
115
+ ```mermaid
116
+ %%{init: {
117
+ 'theme': 'default',
118
+ 'themeVariables': { 'fontSize': '14px', 'subgraphTitleSize': '16px' },
119
+ 'flowchart': { 'useMaxWidth': false, 'htmlLabels': true, 'curve': 'basis' }
120
+ } }%%
121
+ ```
122
+ 3. **Readable Text Wrapping:**
123
+ - Never let a single node have a long horizontal text block. Use `<br/>` tags to stack text vertically.
124
+ - Format nodes with clear visual hierarchy: Use bolding for titles, bracket notation for IDs, and italics for status:
125
+ `NodeName["📅 <b>Windows Scheduler</b><br/>[SYS_03]<br/><i>New Component</i>"]`
126
+
127
+ ### 📐 Layout & Real Estate Rules
128
+ 1. **The 3-Column Kanban Layout (Side-by-Side):**
129
+ - When sequential phases are required (e.g., Phase 1 | Phase 2 | Phase 3) but need to prevent horizontal text compression, utilize a "Hybrid Columnar Layout".
130
+ - **Rule:** Set the top-level flow to Left-to-Right (`flowchart LR`) to align parent containers horizontally, but enforce vertical stacking (`direction TB`) *inside* each subgraph container.
131
+ - **Example Structure:**
132
+ ```mermaid
133
+ flowchart LR
134
+ subgraph Phase1["Phase 1"]
135
+ direction TB
136
+ A --> B --> C
137
+ end
138
+ subgraph Phase2["Phase 2"]
139
+ direction TB
140
+ D --> E --> F
141
+ end
142
+ Phase1 --> Phase2
143
+ ```
144
+ 2. **The Two-Line Grid Layout (Row Stacking):**
145
+ - When a 3-pillar horizontal layout spans too wide, stack them into rows by setting the top-level structure to Top-to-Bottom (`flowchart TD`).
146
+ - Group the lower pillars (Phase 2 and Phase 3) side-by-side inside a helper subgraph container that uses `direction LR`. This forces Phase 2 to wrap onto a new line directly below Phase 1.
147
+ 3. **Component Types:**
148
+ - Use standard flowchart shapes to represent structural entities:
149
+ - `[(Database Node)]` for persistent storage, tables, or file outputs.
150
+ - `["Application Node"]` for processing scripts, APIs, or services.
151
+ - `["🖥️ UI Node"]` for user interfaces.
152
+ 4. **Dynamic Layout & Sizing:** Do not fix exclusively to Left-to-Right (`flowchart LR`) or Top-to-Bottom (`flowchart TD`). Choose the layout dynamically based on component count and readability:
153
+ - Use `flowchart LR` for simple linear flows with few components.
154
+ - Use `flowchart TD` or mixed subgraphs when there are many layers or components to prevent excessive horizontal stretching.
155
+ 5. **PlantUML Engine & Crisp Proportioning:** Diagrams convert via `convert_doc_to_confluence.py` using `scale 1.2` for flowcharts, `scale max 2000 width` for sequence diagrams, `skinparam defaultFontSize 11` (no `skinparam dpi 200` to prevent blowout), natural curved splines (omit `polyline` / `ortho`), tightened whitespace (`nodesep 24`, `ranksep 28`, package `Padding 10`), distinct 1.5px border outlines (`#FEF2F2;line:DC2626` for crimson red impacted borders, `#EFF6FF;line:2563EB` for deep blue existing borders), and cross-package `norank` connectors to guarantee sharp, readable, properly proportioned text in Confluence.
156
+ 6. **No Cluttered Labels or Overlapping Text:** Omit stereotypes (`<<impacted>>`, `<<service>>`) and status tags (`(New Component)`, `(Existing Platform)`) from component boxes and arrow labels. Multi-line edge labels must be wrapped at ~24 characters.
157
+ 7. **No Leaked Tags:** Never emit `<#ghost>` or unsupported formatting tags into Confluence macros. Component and connection classification is handled by the bottom legend.
158
+ 8. **Simplified Technical English (ASD-STE100):** Short sentences, active voice, and clear technical descriptions.