@1aboveio/skills 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -32,6 +32,10 @@ Running `install` with no `--group` opens the interactive bundle picker. The aut
32
32
  - `--dry-run` — print the planned native commands, in order, and run none of them.
33
33
  - `--yes` — answer the native CLI's prompts, including the overwrite prompt, with yes.
34
34
 
35
+ Selecting several groups (interactive picker, repeated `--group`, or `--all`) coalesces to one
36
+ native `skills add` per upstream source, so the installation-method prompt appears once per
37
+ source rather than once per group.
38
+
35
39
  Without `--yes`, an existing same-named skill produces the native overwrite summary and a prompt.
36
40
  Declining leaves the existing content and its links exactly as they were.
37
41
 
@@ -39,7 +43,7 @@ Declining leaves the existing content and its links exactly as they were.
39
43
 
40
44
  - `engineering-workflow` — Engineering Workflow: e2e-test, ensure-coverage, implement-and-pr, resolve-issues, resolve-release, review-pr, smoke, engineering-runtime
41
45
  - `delivery-infrastructure` — Delivery Infrastructure: cloud-build, cloud-debug, cloud-deploy, docker, google-cloud, mergify, podman
42
- - `backend` — Backend: airflow-dag-develop, app-debug, python-backend
46
+ - `backend` — Backend: airflow-dag-develop, app-debug, pyspark, python-backend
43
47
  - `fullstack` — Fullstack: better-auth, monorepo, nextjs-fullstack, prisma-setup, shadcn, zod-v4, impeccable — impeccable is fetched from its upstream owner and not included in this package
44
48
  - `matt-pocock-toolkit` — Matt Pocock Toolkit: batch-grill-me, code-review, codebase-design, diagnosing-bugs, domain-modeling, grill-me, grill-with-docs, grilling, handoff, improve-codebase-architecture, prototype, research, resolving-merge-conflicts, setup-matt-pocock-skills, tdd, to-spec, to-tickets, triage, wayfinder — every member is fetched from its upstream owner and is not included in this package
45
49
 
@@ -65,7 +69,7 @@ npx skills@1.5.22 remove --global --agent universal claude-code --skill e2e-test
65
69
  # Delivery Infrastructure
66
70
  npx skills@1.5.22 remove --global --agent universal claude-code --skill cloud-build cloud-debug cloud-deploy docker google-cloud mergify podman
67
71
  # Backend
68
- npx skills@1.5.22 remove --global --agent universal claude-code --skill airflow-dag-develop app-debug python-backend
72
+ npx skills@1.5.22 remove --global --agent universal claude-code --skill airflow-dag-develop app-debug pyspark python-backend
69
73
  # Fullstack
70
74
  npx skills@1.5.22 remove --global --agent universal claude-code --skill better-auth monorepo nextjs-fullstack prisma-setup shadcn zod-v4 impeccable
71
75
  # Matt Pocock Toolkit
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@1aboveio/skills",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "Install the 1AboveIO first-party skill groups through the native Skills CLI.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -8,7 +8,7 @@
8
8
  "id": "first-party",
9
9
  "type": "first-party",
10
10
  "package": "@1aboveio/skills",
11
- "version": "0.10.0",
11
+ "version": "0.11.0",
12
12
  "updatePolicy": "pinned-npm-version",
13
13
  "members": [
14
14
  {
@@ -113,6 +113,12 @@
113
113
  "role": "skill",
114
114
  "licenseResponsibility": "1aboveio-mit"
115
115
  },
116
+ {
117
+ "installName": "pyspark",
118
+ "path": "skills/backend/pyspark",
119
+ "role": "skill",
120
+ "licenseResponsibility": "1aboveio-mit"
121
+ },
116
122
  {
117
123
  "installName": "python-backend",
118
124
  "path": "skills/backend/python-backend",
@@ -448,7 +454,7 @@
448
454
  "sourceId": "first-party",
449
455
  "sourceType": "first-party",
450
456
  "package": "@1aboveio/skills",
451
- "version": "0.10.0",
457
+ "version": "0.11.0",
452
458
  "installPath": null,
453
459
  "members": [
454
460
  "harness-runtime",
@@ -520,7 +526,7 @@
520
526
  "sourceId": "first-party",
521
527
  "sourceType": "first-party",
522
528
  "package": "@1aboveio/skills",
523
- "version": "0.10.0",
529
+ "version": "0.11.0",
524
530
  "installPath": null,
525
531
  "members": [
526
532
  "harness-runtime",
@@ -564,7 +570,7 @@
564
570
  "sourceId": "first-party",
565
571
  "sourceType": "first-party",
566
572
  "package": "@1aboveio/skills",
567
- "version": "0.10.0",
573
+ "version": "0.11.0",
568
574
  "installPath": null,
569
575
  "members": [
570
576
  "harness-runtime",
@@ -597,7 +603,7 @@
597
603
  "sourceId": "first-party",
598
604
  "sourceType": "first-party",
599
605
  "package": "@1aboveio/skills",
600
- "version": "0.10.0",
606
+ "version": "0.11.0",
601
607
  "installPath": null,
602
608
  "members": [
603
609
  "harness-runtime",
@@ -640,18 +646,19 @@
640
646
  "sourceId": "first-party",
641
647
  "sourceType": "first-party",
642
648
  "package": "@1aboveio/skills",
643
- "version": "0.10.0",
649
+ "version": "0.11.0",
644
650
  "installPath": null,
645
651
  "members": [
646
652
  "harness-runtime",
647
653
  "airflow-dag-develop",
648
654
  "app-debug",
655
+ "pyspark",
649
656
  "python-backend"
650
657
  ],
651
658
  "commands": [
652
659
  {
653
660
  "transport": "local",
654
- "command": "npx skills@1.5.22 add . --global --agent universal claude-code --skill harness-runtime airflow-dag-develop app-debug python-backend"
661
+ "command": "npx skills@1.5.22 add . --global --agent universal claude-code --skill harness-runtime airflow-dag-develop app-debug pyspark python-backend"
655
662
  }
656
663
  ],
657
664
  "onFailure": {
@@ -669,18 +676,19 @@
669
676
  "sourceId": "first-party",
670
677
  "sourceType": "first-party",
671
678
  "package": "@1aboveio/skills",
672
- "version": "0.10.0",
679
+ "version": "0.11.0",
673
680
  "installPath": null,
674
681
  "members": [
675
682
  "harness-runtime",
676
683
  "airflow-dag-develop",
677
684
  "app-debug",
685
+ "pyspark",
678
686
  "python-backend"
679
687
  ],
680
688
  "commands": [
681
689
  {
682
690
  "transport": "local",
683
- "command": "npx skills@1.5.22 add . --global --agent universal claude-code --skill harness-runtime airflow-dag-develop app-debug python-backend"
691
+ "command": "npx skills@1.5.22 add . --global --agent universal claude-code --skill harness-runtime airflow-dag-develop app-debug pyspark python-backend"
684
692
  }
685
693
  ],
686
694
  "onFailure": {
@@ -691,7 +699,7 @@
691
699
  ]
692
700
  },
693
701
  "uninstall": {
694
- "command": "npx skills@1.5.22 remove --global --agent universal claude-code --skill airflow-dag-develop app-debug python-backend",
702
+ "command": "npx skills@1.5.22 remove --global --agent universal claude-code --skill airflow-dag-develop app-debug pyspark python-backend",
695
703
  "removesDependencies": false
696
704
  }
697
705
  },
@@ -708,7 +716,7 @@
708
716
  "sourceId": "first-party",
709
717
  "sourceType": "first-party",
710
718
  "package": "@1aboveio/skills",
711
- "version": "0.10.0",
719
+ "version": "0.11.0",
712
720
  "installPath": null,
713
721
  "members": [
714
722
  "harness-runtime",
@@ -760,7 +768,7 @@
760
768
  "sourceId": "first-party",
761
769
  "sourceType": "first-party",
762
770
  "package": "@1aboveio/skills",
763
- "version": "0.10.0",
771
+ "version": "0.11.0",
764
772
  "installPath": null,
765
773
  "members": [
766
774
  "harness-runtime",
@@ -916,7 +924,7 @@
916
924
  "sourceId": "first-party",
917
925
  "sourceType": "first-party",
918
926
  "package": "@1aboveio/skills",
919
- "version": "0.10.0",
927
+ "version": "0.11.0",
920
928
  "installPath": null,
921
929
  "members": [
922
930
  "harness-runtime"
@@ -942,7 +950,7 @@
942
950
  "sourceId": "first-party",
943
951
  "sourceType": "first-party",
944
952
  "package": "@1aboveio/skills",
945
- "version": "0.10.0",
953
+ "version": "0.11.0",
946
954
  "installPath": null,
947
955
  "members": [
948
956
  "harness-runtime"
@@ -1012,7 +1020,7 @@
1012
1020
  "sourceId": "first-party",
1013
1021
  "sourceType": "first-party",
1014
1022
  "package": "@1aboveio/skills",
1015
- "version": "0.10.0",
1023
+ "version": "0.11.0",
1016
1024
  "installPath": null,
1017
1025
  "members": [
1018
1026
  "harness-runtime",
@@ -1033,6 +1041,7 @@
1033
1041
  "podman",
1034
1042
  "airflow-dag-develop",
1035
1043
  "app-debug",
1044
+ "pyspark",
1036
1045
  "python-backend",
1037
1046
  "better-auth",
1038
1047
  "monorepo",
@@ -1044,7 +1053,7 @@
1044
1053
  "commands": [
1045
1054
  {
1046
1055
  "transport": "local",
1047
- "command": "npx skills@1.5.22 add . --global --agent universal claude-code --skill harness-runtime e2e-test ensure-coverage implement-and-pr resolve-issues resolve-release review-pr smoke engineering-runtime cloud-build cloud-debug cloud-deploy docker google-cloud mergify podman airflow-dag-develop app-debug python-backend better-auth monorepo nextjs-fullstack prisma-setup shadcn zod-v4 && npm ci --ignore-scripts --prefix \"$HOME/.agents/skills/engineering-runtime/scripts\""
1056
+ "command": "npx skills@1.5.22 add . --global --agent universal claude-code --skill harness-runtime e2e-test ensure-coverage implement-and-pr resolve-issues resolve-release review-pr smoke engineering-runtime cloud-build cloud-debug cloud-deploy docker google-cloud mergify podman airflow-dag-develop app-debug pyspark python-backend better-auth monorepo nextjs-fullstack prisma-setup shadcn zod-v4 && npm ci --ignore-scripts --prefix \"$HOME/.agents/skills/engineering-runtime/scripts\""
1048
1057
  }
1049
1058
  ],
1050
1059
  "onFailure": {
@@ -1120,7 +1129,7 @@
1120
1129
  "sourceId": "first-party",
1121
1130
  "sourceType": "first-party",
1122
1131
  "package": "@1aboveio/skills",
1123
- "version": "0.10.0",
1132
+ "version": "0.11.0",
1124
1133
  "installPath": null,
1125
1134
  "members": [
1126
1135
  "harness-runtime",
@@ -1141,6 +1150,7 @@
1141
1150
  "podman",
1142
1151
  "airflow-dag-develop",
1143
1152
  "app-debug",
1153
+ "pyspark",
1144
1154
  "python-backend",
1145
1155
  "better-auth",
1146
1156
  "monorepo",
@@ -1152,7 +1162,7 @@
1152
1162
  "commands": [
1153
1163
  {
1154
1164
  "transport": "local",
1155
- "command": "npx skills@1.5.22 add . --global --agent universal claude-code --skill harness-runtime e2e-test ensure-coverage implement-and-pr resolve-issues resolve-release review-pr smoke engineering-runtime cloud-build cloud-debug cloud-deploy docker google-cloud mergify podman airflow-dag-develop app-debug python-backend better-auth monorepo nextjs-fullstack prisma-setup shadcn zod-v4 && npm ci --ignore-scripts --prefix \"$HOME/.agents/skills/engineering-runtime/scripts\""
1165
+ "command": "npx skills@1.5.22 add . --global --agent universal claude-code --skill harness-runtime e2e-test ensure-coverage implement-and-pr resolve-issues resolve-release review-pr smoke engineering-runtime cloud-build cloud-debug cloud-deploy docker google-cloud mergify podman airflow-dag-develop app-debug pyspark python-backend better-auth monorepo nextjs-fullstack prisma-setup shadcn zod-v4 && npm ci --ignore-scripts --prefix \"$HOME/.agents/skills/engineering-runtime/scripts\""
1156
1166
  }
1157
1167
  ],
1158
1168
  "onFailure": {
@@ -1183,7 +1193,7 @@
1183
1193
  ]
1184
1194
  },
1185
1195
  "uninstall": {
1186
- "command": "npx skills@1.5.22 remove --global --agent universal claude-code --skill e2e-test ensure-coverage implement-and-pr resolve-issues resolve-release review-pr smoke engineering-runtime cloud-build cloud-debug cloud-deploy docker google-cloud mergify podman airflow-dag-develop app-debug python-backend better-auth monorepo nextjs-fullstack prisma-setup shadcn zod-v4 impeccable batch-grill-me code-review codebase-design diagnosing-bugs domain-modeling grill-me grill-with-docs grilling handoff improve-codebase-architecture prototype research resolving-merge-conflicts setup-matt-pocock-skills tdd to-spec to-tickets triage wayfinder harness-runtime"
1196
+ "command": "npx skills@1.5.22 remove --global --agent universal claude-code --skill e2e-test ensure-coverage implement-and-pr resolve-issues resolve-release review-pr smoke engineering-runtime cloud-build cloud-debug cloud-deploy docker google-cloud mergify podman airflow-dag-develop app-debug pyspark python-backend better-auth monorepo nextjs-fullstack prisma-setup shadcn zod-v4 impeccable batch-grill-me code-review codebase-design diagnosing-bugs domain-modeling grill-me grill-with-docs grilling handoff improve-codebase-architecture prototype research resolving-merge-conflicts setup-matt-pocock-skills tdd to-spec to-tickets triage wayfinder harness-runtime"
1187
1197
  }
1188
1198
  }
1189
1199
  }
@@ -85,40 +85,108 @@ function localFirstPartyCommand(command, localRoot, groupId) {
85
85
  return command.replace(NATIVE_ADD_SOURCE, (_match, prefix) => `${prefix}${shellArgument(localRoot)}`);
86
86
  }
87
87
 
88
+ // Native `skills add` prompts once per invocation for the installation method. Selecting several
89
+ // bundles used to emit one add per group, so interactive installs asked the same question N times.
90
+ // Coalesce by sourceId: one native add per upstream, with the union of --skill names, in the order
91
+ // sources first appear across the selection. Identical full commands still collapse as before.
92
+ const SKILL_TAIL = /^(.*? --skill )(.+?)( && .*)?$/;
93
+
94
+ function skillsFromCommand(command) {
95
+ const match = SKILL_TAIL.exec(command);
96
+ if (!match) throw new Error(`Native add has no --skill list to coalesce: ${command}`);
97
+ return match[2].trim().split(/\s+/).filter(Boolean);
98
+ }
99
+
100
+ function withSkills(command, skills) {
101
+ const match = SKILL_TAIL.exec(command);
102
+ if (!match) throw new Error(`Native add has no --skill list to rewrite: ${command}`);
103
+ return `${match[1]}${skills.join(' ')}${match[3] || ''}`;
104
+ }
105
+
106
+ function withYesFlag(command) {
107
+ // --yes must bind to `skills`, not to a trailing `&& npm ci ...`.
108
+ const match = SKILL_TAIL.exec(command);
109
+ if (!match) return `${command} --yes`;
110
+ return `${match[1]}${match[2]} --yes${match[3] || ''}`;
111
+ }
112
+
113
+ function mergeSkillLists(existing, incoming) {
114
+ const seen = new Set(existing);
115
+ const merged = [...existing];
116
+ for (const skill of incoming) {
117
+ if (seen.has(skill)) continue;
118
+ seen.add(skill);
119
+ merged.push(skill);
120
+ }
121
+ return merged;
122
+ }
123
+
88
124
  export function planBundleCommands(action, groupIds, { yes = false, ...transport } = {}) {
89
125
  if (!ACTIONS.has(action)) throw new Error(`Unsupported bundle action: ${action}`);
90
126
  const { recipesPath, localRoot } = resolveBundleTransport(transport);
91
127
  const recipes = readRecipes(recipesPath);
92
128
  assertLocalRoot(localRoot);
93
129
  const groups = new Map(recipes.groups.map((group) => [group.id, group]));
94
- const commands = [];
95
- const seen = new Set();
96
-
130
+ const requested = [];
97
131
  for (const groupId of groupIds) {
132
+ if (!groups.has(groupId) || SUPPORT_GROUPS.has(groupId)) throw new Error(`Unknown bundle: ${groupId}`);
133
+ if (!requested.includes(groupId)) requested.push(groupId);
134
+ }
135
+
136
+ // Always coalesce from the selected groups (not recipes.all). A public package may expose only a
137
+ // subset of groups while still shipping a full recipes.all from the private catalog; using that
138
+ // all-plan would install skills that are not in the selection / not in the package.
139
+ const bySource = new Map();
140
+ const sourceOrder = [];
141
+
142
+ for (const groupId of requested) {
98
143
  const group = groups.get(groupId);
99
- if (!group || SUPPORT_GROUPS.has(groupId)) throw new Error(`Unknown bundle: ${groupId}`);
100
144
  for (const step of group[action].steps) {
101
145
  const generated = COMMAND_TRANSPORTS
102
- .map((transport) => step.commands.find((candidate) => candidate.transport === transport))
146
+ .map((transportName) => step.commands.find((candidate) => candidate.transport === transportName))
103
147
  .find(Boolean);
104
148
  if (!generated) throw new Error(`Bundle ${groupId} has no supported command for source ${step.sourceId}`);
105
- let command = step.sourceId === 'first-party'
149
+ const command = step.sourceId === 'first-party'
106
150
  ? localFirstPartyCommand(generated.command, localRoot, groupId)
107
151
  : generated.command;
108
- if (yes) command += ' --yes';
109
- if (seen.has(command)) continue;
110
- seen.add(command);
111
- commands.push({
112
- groupId,
113
- groupName: group.displayName,
114
- sourceId: step.sourceId,
115
- command,
116
- cwd: localRoot,
117
- failureMessage: step.onFailure.message,
118
- });
152
+ const skills = skillsFromCommand(command);
153
+ const suffix = SKILL_TAIL.exec(command)?.[3] || '';
154
+ const existing = bySource.get(step.sourceId);
155
+ if (!existing) {
156
+ sourceOrder.push(step.sourceId);
157
+ bySource.set(step.sourceId, {
158
+ groupIds: [groupId],
159
+ groupNames: [group.displayName],
160
+ sourceId: step.sourceId,
161
+ template: command,
162
+ skills: [...skills],
163
+ suffix,
164
+ cwd: localRoot,
165
+ failureMessage: step.onFailure.message,
166
+ });
167
+ continue;
168
+ }
169
+ existing.groupIds.push(groupId);
170
+ if (!existing.groupNames.includes(group.displayName)) existing.groupNames.push(group.displayName);
171
+ existing.skills = mergeSkillLists(existing.skills, skills);
172
+ if (!existing.suffix && suffix) existing.suffix = suffix;
119
173
  }
120
174
  }
121
- return commands;
175
+
176
+ return sourceOrder.map((sourceId) => {
177
+ const planned = bySource.get(sourceId);
178
+ let command = withSkills(planned.template, planned.skills);
179
+ if (planned.suffix && !command.includes(' && ')) command = `${command}${planned.suffix}`;
180
+ if (yes) command = withYesFlag(command);
181
+ return {
182
+ groupId: planned.groupIds.length === 1 ? planned.groupIds[0] : planned.groupIds.join('+'),
183
+ groupName: planned.groupNames.length === 1 ? planned.groupNames[0] : planned.groupNames.join(' + '),
184
+ sourceId: planned.sourceId,
185
+ command,
186
+ cwd: planned.cwd,
187
+ failureMessage: planned.failureMessage,
188
+ };
189
+ });
122
190
  }
123
191
 
124
192
  export function runBundleCommands(commands, {
@@ -0,0 +1,3 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 1AboveIO
@@ -0,0 +1,116 @@
1
+ ---
2
+ name: pyspark
3
+ description: Implement, debug, review, and optimize Apache Spark or PySpark batch jobs, ETL pipelines, feature calculations, and warehouse writes. Use whenever work involves Spark DataFrames or SQL, joins, windows, grouped state, UDFs, shuffle, skew, spill, slow stages, Spark Connect, spark-submit, Hudi publication, backfills, semantic parity, or production canaries, even when the request is framed only as a warehouse timeout or performance regression.
4
+ ---
5
+
6
+ # PySpark
7
+
8
+ Use this skill as the workflow and routing index. Read the referenced playbooks
9
+ that match the task before editing code or running a production-scale job.
10
+
11
+ ## Workflow
12
+
13
+ ```text
14
+ contract -> transform -> materialize -> test -> CI/CD
15
+ ```
16
+
17
+ Follow all five steps in order.
18
+
19
+ ### 1. Contract
20
+
21
+ - **Input:** declare all parameters, defaults, validation, source snapshot/date
22
+ scope, overlapping-run behavior, idempotency/single-writer rules, and exact
23
+ Python and PySpark versions.
24
+ - **Output:** declare plain Spark versus Hudi table type, output identity/schema,
25
+ partition columns, record key, deterministic order or precombine field, and
26
+ append/upsert/overwrite write mode.
27
+ - **Conventions:** declare native Spark versus Spark Connect and full rebuild
28
+ versus incremental processing, including the incremental watermark,
29
+ lookback/checkpoint, and restart boundary when applicable.
30
+
31
+ Read [ETL Contract](references/etl-contract.md). When the repository has no ETL
32
+ base module, adapt [`assets/templates/etl.py`](assets/templates/etl.py).
33
+
34
+ ### 2. Transform
35
+
36
+ - Make `transform` accept DataFrames and return the complete candidate without
37
+ invoking validation or a writer.
38
+ - Estimate rows after joins/explodes/groups and measure maximum rows per key.
39
+ Fix algorithmic expansion and hot keys before tuning Spark configuration.
40
+ - Prefer built-in expressions and windows. Use exact bucketing or one sorted
41
+ grouped sweep only for state that built-ins cannot express cleanly.
42
+ - Preserve complete row identity, ordering, output columns, and required
43
+ behavior.
44
+ - Avoid explicit `.cache()` and `.persist()` by default.
45
+
46
+ Read [Transformation Design](references/transformation-design.md). For temporal
47
+ windows or FIFO paths, also read
48
+ [Velocity Feature Calculation](references/velocity-feature-calculation.md).
49
+
50
+ ### 3. Materialize
51
+
52
+ - Treat materialization as the Spark action that executes the lazy candidate,
53
+ not as caching.
54
+ - Use one aggregate validation action before every writer. Reference every
55
+ derived output so Catalyst cannot prune untested calculations.
56
+ - Check row count, distinct/null/duplicate identities, domain blockers, and
57
+ required date/partition scope. Fail closed on any blocker.
58
+ - Bind validation and publication to the same immutable source snapshot because
59
+ Spark may recompute the candidate.
60
+ - Keep the validate-only guard inside the job and before every writer. Do not
61
+ add `.count()`, `.cache()`, or `.persist()` to warm execution.
62
+
63
+ Read [Production Validation](references/production-validation.md).
64
+
65
+ ### 4. Test
66
+
67
+ 1. **Local test:** run pure state, tiny real-Spark, plan-shape, focused repository,
68
+ and bidirectional parity tests. Cover boundaries, ties, nulls, arithmetic,
69
+ identity, and precedence.
70
+ 2. **Cluster canary test:** run the production entry point and source scope with
71
+ `--validate-only`. Require zero blockers, an explicit publication skip, zero
72
+ Spark output records/bytes, and readable driver/event logs.
73
+ 3. **Profiling:** measure an isolated baseline, parse the canary event log, and
74
+ compare both runs on the same source scope and resource profile. Record
75
+ wall/stage/task time, cardinality, shuffle, spill, skew, output, and target
76
+ plan patterns.
77
+
78
+ ```bash
79
+ spark-submit <production-options> job.py --validate-only
80
+ set -o pipefail
81
+ hdfs dfs -cat "$EVENT_LOG" | \
82
+ python {baseDir}/scripts/spark_eventlog_summary.py > canary-summary.json
83
+ ```
84
+
85
+ Reject missing evidence, incomparable scope, unexplained regressions, or a
86
+ missed performance gate. Keep validate-only time separate from write time.
87
+
88
+ Read [Parity Testing](references/parity-testing.md),
89
+ [Diagnosis And Profiling](references/diagnosis-and-profiling.md), and
90
+ [Production Validation](references/production-validation.md).
91
+
92
+ ### 5. CI/CD
93
+
94
+ 1. Open a PR with required-behavior, test, canary, profiling, and risk evidence.
95
+ 2. Run required CI and independent review; fix failures.
96
+ 3. Enqueue through the configured merge queue and merge without bypassing gates.
97
+ 4. Deploy only the merged commit through the normal deployment path.
98
+ 5. Run write-enabled production validation only with explicit authorization;
99
+ reconcile counts, identity, schema, coverage, blockers, readability, write
100
+ mode, and commit evidence independently.
101
+ 6. Restore scheduler/process ownership and report compute/validation,
102
+ publication, and end-to-end time separately.
103
+
104
+ Read [Production Validation](references/production-validation.md).
105
+
106
+ ## Reference Index
107
+
108
+ | Need | Read or run |
109
+ |---|---|
110
+ | Define or review an ETL job contract | [ETL Contract](references/etl-contract.md) and `assets/templates/etl.py` |
111
+ | Diagnose or profile | [Diagnosis And Profiling](references/diagnosis-and-profiling.md) |
112
+ | Design transformations | [Transformation Design](references/transformation-design.md) |
113
+ | Optimize temporal/FIFO state | [Velocity Feature Calculation](references/velocity-feature-calculation.md) |
114
+ | Prove parity | [Parity Testing](references/parity-testing.md) |
115
+ | Run canaries or writes | [Production Validation](references/production-validation.md) |
116
+ | Summarize an event log | `scripts/spark_eventlog_summary.py` |