@aiaiai-pt/frankctl 0.7.2 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/README.md +14 -7
- package/dist/index.js +222 -221
- package/dist/template-packs/cira/11_stop_time.yaml +109 -0
- package/dist/template-packs/cira/template-pack.yaml +10 -0
- package/package.json +1 -1
- package/skills/frank-cli/SKILL.md +70 -9
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Stage 11: all 237,808 regional schedule rows projected to the existing B3
|
|
2
|
+
# stop_time type. The source _record_id is generated once from the declared
|
|
3
|
+
# composite stream key (trip_id, Ordem, IdParagem), persisted in Bronze, and
|
|
4
|
+
# passed through unchanged here. Point-in-time profile: 237,808 non-null and
|
|
5
|
+
# distinct _record_id values, 8,964 trip keys, 2,411 stop keys, and zero
|
|
6
|
+
# relationship misses. These counts are evidence, never row limits.
|
|
7
|
+
|
|
8
|
+
apiVersion: frank.platform/v1
|
|
9
|
+
kind: IdentityPolicy
|
|
10
|
+
metadata:
|
|
11
|
+
name: cira-stop-time-external-id
|
|
12
|
+
spec:
|
|
13
|
+
name: cira-stop-time-external-id
|
|
14
|
+
strategy: passthrough
|
|
15
|
+
source_fields:
|
|
16
|
+
- _record_id
|
|
17
|
+
normalizers: []
|
|
18
|
+
emit_format: "cira:bdtransportes:stop_time:{resolved_key}"
|
|
19
|
+
collision_policy: error
|
|
20
|
+
---
|
|
21
|
+
apiVersion: frank.platform/v1
|
|
22
|
+
kind: Pipeline
|
|
23
|
+
metadata:
|
|
24
|
+
name: cira_transport_stop_times
|
|
25
|
+
spec:
|
|
26
|
+
name: cira_transport_stop_times
|
|
27
|
+
description: Complete CIRA regional stop times with stable source identity and existing trip/stop links.
|
|
28
|
+
source_ids:
|
|
29
|
+
- cira-bdtransportes-horarios
|
|
30
|
+
schedule_config:
|
|
31
|
+
type: manual
|
|
32
|
+
steps:
|
|
33
|
+
- name: stop_time_rows
|
|
34
|
+
kind: field_mapping
|
|
35
|
+
params:
|
|
36
|
+
field_mappings:
|
|
37
|
+
- target_column: external_id
|
|
38
|
+
mapping_kind: identity
|
|
39
|
+
identity_policy:
|
|
40
|
+
name: cira-stop-time-external-id
|
|
41
|
+
field_order: 0
|
|
42
|
+
- source_column: schedule
|
|
43
|
+
target_column: arrival_time
|
|
44
|
+
mapping_kind: source_expression
|
|
45
|
+
field_order: 1
|
|
46
|
+
- source_column: schedule
|
|
47
|
+
target_column: departure_time
|
|
48
|
+
mapping_kind: source_expression
|
|
49
|
+
field_order: 2
|
|
50
|
+
- source_column: ordem
|
|
51
|
+
target_column: stop_sequence
|
|
52
|
+
mapping_kind: source_expression
|
|
53
|
+
custom_sql: "try_cast(${source} AS integer)"
|
|
54
|
+
field_order: 3
|
|
55
|
+
- source_column: trip_id
|
|
56
|
+
target_column: trip_id
|
|
57
|
+
mapping_kind: source_expression
|
|
58
|
+
field_order: 4
|
|
59
|
+
- source_column: id_paragem
|
|
60
|
+
target_column: stop_key
|
|
61
|
+
mapping_kind: source_expression
|
|
62
|
+
custom_sql: "concat('stop:',cast(try_cast(${source} AS bigint) AS varchar))"
|
|
63
|
+
field_order: 5
|
|
64
|
+
depends_on: []
|
|
65
|
+
emits_to: backing_dataset
|
|
66
|
+
config:
|
|
67
|
+
output_layer: silver
|
|
68
|
+
---
|
|
69
|
+
apiVersion: frank.platform/v1
|
|
70
|
+
kind: BackingDataset
|
|
71
|
+
metadata:
|
|
72
|
+
name: cira_transport_stop_times
|
|
73
|
+
dependsOn:
|
|
74
|
+
- entityTypeId: transit_trip
|
|
75
|
+
ontologyTenantId: cira
|
|
76
|
+
- entityTypeId: transit_stop
|
|
77
|
+
ontologyTenantId: cira
|
|
78
|
+
spec:
|
|
79
|
+
entity_type_id: stop_time
|
|
80
|
+
entity_type_name: Stop Time
|
|
81
|
+
ontology_tenant_id: cira
|
|
82
|
+
pipeline: cira_transport_stop_times
|
|
83
|
+
sync_mode: manual
|
|
84
|
+
primary_key_column: external_id
|
|
85
|
+
title_key_column: external_id
|
|
86
|
+
property_mappings:
|
|
87
|
+
- column: external_id
|
|
88
|
+
property: external_id
|
|
89
|
+
type: varchar
|
|
90
|
+
is_primary_key: true
|
|
91
|
+
- column: arrival_time
|
|
92
|
+
property: arrival_time
|
|
93
|
+
type: varchar
|
|
94
|
+
- column: departure_time
|
|
95
|
+
property: departure_time
|
|
96
|
+
type: varchar
|
|
97
|
+
- column: stop_sequence
|
|
98
|
+
property: stop_sequence
|
|
99
|
+
type: integer
|
|
100
|
+
- column: trip_id
|
|
101
|
+
property: trip
|
|
102
|
+
is_relationship: true
|
|
103
|
+
target_type: transit_trip
|
|
104
|
+
target_key: service_id
|
|
105
|
+
- column: stop_key
|
|
106
|
+
property: stop
|
|
107
|
+
is_relationship: true
|
|
108
|
+
target_type: transit_stop
|
|
109
|
+
target_key: code
|
|
@@ -98,6 +98,16 @@ spec:
|
|
|
98
98
|
- 580
|
|
99
99
|
- 591
|
|
100
100
|
- 592
|
|
101
|
+
regional-transport-stop-times:
|
|
102
|
+
title: Regional transport stop times with declarative identity
|
|
103
|
+
status: declared_pending
|
|
104
|
+
manifests:
|
|
105
|
+
- 11_stop_time.yaml
|
|
106
|
+
required_sources:
|
|
107
|
+
- cira-bdtransportes-horarios
|
|
108
|
+
issues:
|
|
109
|
+
- 592
|
|
110
|
+
- 650
|
|
101
111
|
regional-transport-avl:
|
|
102
112
|
title: Regional transport current vehicle positions
|
|
103
113
|
status: live_proven
|
package/package.json
CHANGED
|
@@ -85,20 +85,22 @@ an explicit subset. Repeat `--set frank_tenant_prefix=<8-hex>` and
|
|
|
85
85
|
ontology tenant. The CIRA pack treats ontology schema as externally owned and therefore
|
|
86
86
|
contains no `ensure_schema` mutation.
|
|
87
87
|
|
|
88
|
-
A pipeline is one multi-document YAML file describing
|
|
88
|
+
A pipeline is one multi-document YAML file describing four kinds of object under the
|
|
89
89
|
`frank.platform/v1` API version:
|
|
90
90
|
|
|
91
91
|
| `kind` | Role |
|
|
92
92
|
|--------|------|
|
|
93
93
|
| `Source` | Extract/Load config — connector `pattern_id`, streams, schedule → Iceberg **bronze** |
|
|
94
|
+
| `IdentityPolicy` | Stable-key recipe reconciled and pinned before a consuming transform |
|
|
94
95
|
| `Pipeline` | Transform steps (SQL/Python) turning bronze → **silver/gold** |
|
|
95
96
|
| `BackingDataset` | Maps a silver table's columns to **ontology** entity properties |
|
|
96
97
|
|
|
97
98
|
Objects reference each other by `metadata.name`, so one file can be self-contained and
|
|
98
|
-
version-controllable.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
the opaque
|
|
99
|
+
version-controllable. Credential behavior follows explicit `SOURCE_CREDENTIAL_MODE`:
|
|
100
|
+
current base Compose uses `legacy_inline`, where write-only values persist in redacted
|
|
101
|
+
`source_config` and `sources credentials set|rotate` is unavailable; immutable release
|
|
102
|
+
`vault` mode uses those commands and keeps only the opaque `credential_ref` in exports.
|
|
103
|
+
Commit only reviewed, secret-free declarations in either mode.
|
|
102
104
|
|
|
103
105
|
### The loop: scaffold → edit → apply
|
|
104
106
|
|
|
@@ -139,8 +141,8 @@ separate target entity readback.
|
|
|
139
141
|
|
|
140
142
|
| Flag | What it does |
|
|
141
143
|
|------|--------------|
|
|
142
|
-
| `--dry-run` | Client-side parse + cross-ref
|
|
143
|
-
| `--dry-run-server` | Server-side preflight against the catalog + DB without persisting. Catches unknown `pattern_id`s, would-be immutable-diff conflicts, and unresolved refs. |
|
|
144
|
+
| `--dry-run` | Client-side parse + local cross-ref validation. Catches malformed YAML and broken same-file references. No server call. |
|
|
145
|
+
| `--dry-run-server` | Server-side preflight against the catalog + DB without persisting. Catches unknown `pattern_id`s, identity-policy create/noop/patch/new-version state, would-be immutable-diff conflicts, and unresolved refs. |
|
|
144
146
|
| `--dry-run-server --live-ontology` | Adds an opt-in, GET-only check of ontology tenant, type, properties, relationships, keys, and declared carrier types. It never changes ontology schema or entities. |
|
|
145
147
|
|
|
146
148
|
Run `--dry-run-server` in CI on every change; run `apply --wait` to promote.
|
|
@@ -157,6 +159,60 @@ BackingDataset and set `spec.sync_mode: manual`. `--wait` topologically orders s
|
|
|
157
159
|
dependencies and requires external dependencies in the same ontology tenant to be
|
|
158
160
|
already `synced`. Cycles and self-referential relationships fail before execution.
|
|
159
161
|
|
|
162
|
+
### Stable identity in declarative pipelines
|
|
163
|
+
|
|
164
|
+
Declare a tenant-owned recipe once, then reference it by name from an identity field
|
|
165
|
+
mapping. `frankctl` applies in `Source → IdentityPolicy → Pipeline → BackingDataset`
|
|
166
|
+
order and replaces the symbolic reference with the exact returned policy ID/version;
|
|
167
|
+
UUIDs do not belong in tracked YAML.
|
|
168
|
+
|
|
169
|
+
```yaml
|
|
170
|
+
apiVersion: frank.platform/v1
|
|
171
|
+
kind: IdentityPolicy
|
|
172
|
+
metadata: { name: source-record-id }
|
|
173
|
+
spec:
|
|
174
|
+
name: source-record-id
|
|
175
|
+
strategy: passthrough
|
|
176
|
+
source_fields: [_record_id]
|
|
177
|
+
normalizers: []
|
|
178
|
+
emit_format: "my-provider:my-type:{resolved_key}"
|
|
179
|
+
collision_policy: error
|
|
180
|
+
---
|
|
181
|
+
apiVersion: frank.platform/v1
|
|
182
|
+
kind: Pipeline
|
|
183
|
+
metadata: { name: my-pipeline }
|
|
184
|
+
spec:
|
|
185
|
+
source_ids: [my-source]
|
|
186
|
+
steps:
|
|
187
|
+
- name: project
|
|
188
|
+
kind: field_mapping
|
|
189
|
+
params:
|
|
190
|
+
field_mappings:
|
|
191
|
+
- target_column: external_id
|
|
192
|
+
mapping_kind: identity
|
|
193
|
+
identity_policy: { name: source-record-id }
|
|
194
|
+
field_order: 0
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Same-file references contain only `name`; external policies require exact `{name, tier,
|
|
198
|
+
version}`. Never author raw UUID/version/hash pins: `frankctl` resolves symbolic YAML
|
|
199
|
+
and carries the server-returned recipe hash as an internal optimistic precondition.
|
|
200
|
+
|
|
201
|
+
Policy reconciliation reports `create`, `noop`, `patch`, or `new-version`. First use freezes
|
|
202
|
+
a recipe globally; later changes version it. Reads compute tenant-live `used_in`, including
|
|
203
|
+
for system policies, without overwriting a shared system cache. Multi-document apply is
|
|
204
|
+
sequential, not atomic: correct a failure and reapply the complete declaration.
|
|
205
|
+
|
|
206
|
+
Pipeline activation locks Pipeline, affected Transforms in deterministic UUID order with
|
|
207
|
+
refreshed mapping/source snapshots, then old+new policies in UUID/version order. Direct
|
|
208
|
+
Transform PUT/DELETE locks Transform, then sorted policies. This prevents deadlock and
|
|
209
|
+
stale `used_in`. A changed recipe still returns `409 identity_policy_recipe_changed`;
|
|
210
|
+
reapply the complete file. Dry-run observes state but does not reserve it.
|
|
211
|
+
|
|
212
|
+
Migration `s43_identity_policy_frozen` scans only historical `mapping_kind: identity` pins.
|
|
213
|
+
It recognizes canonical/uppercase/hyphenless/braced/URN UUIDs and normalized positive
|
|
214
|
+
versions without casting malformed JSON. Author symbolically; never mutate ontology schema.
|
|
215
|
+
|
|
160
216
|
### Immutable state & when to force
|
|
161
217
|
|
|
162
218
|
Backing Datasets carry operational state (ontology cursor, sync history). If a change
|
|
@@ -179,8 +235,10 @@ to a file for review:
|
|
|
179
235
|
frankctl pipelines export <pipeline-id> > pipeline.yaml
|
|
180
236
|
```
|
|
181
237
|
|
|
182
|
-
The export includes ordinary Source configuration, opaque credential references,
|
|
183
|
-
config-only stream declarations,
|
|
238
|
+
The export includes ordinary Source configuration, opaque credential references,
|
|
239
|
+
config-only stream declarations, complete Pipeline steps, and portable symbolic
|
|
240
|
+
IdentityPolicy references. It omits credential values, stream runtime state, and
|
|
241
|
+
tenant-local policy UUIDs.
|
|
184
242
|
|
|
185
243
|
### Minimal manifest shape
|
|
186
244
|
|
|
@@ -429,6 +487,9 @@ you can review, edit, and feed straight into `pipelines apply`.
|
|
|
429
487
|
- **401 after login**: your token expired and refresh failed — `frankctl auth login` again.
|
|
430
488
|
- **`409 immutable_diff` on apply**: a Backing Dataset change would destroy state; see
|
|
431
489
|
`--allow-recreate` above and decide deliberately.
|
|
490
|
+
- **`409 identity_policy_recipe_changed` on apply**: replay the complete manifest so
|
|
491
|
+
policy reconciliation returns a fresh pin; do not add runtime pins to YAML or retry
|
|
492
|
+
only the Pipeline request.
|
|
432
493
|
- **`apply` did nothing**: that's idempotency — the declared state already matches. Use
|
|
433
494
|
`-v` to see the reconcile plan, or `--dry-run-server` to preview.
|
|
434
495
|
|