@serviceai/api-spec 1.1.19 → 1.1.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.
package/openapi.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "openapi": "3.1.0",
3
3
  "info": {
4
4
  "title": "ServiceAi API",
5
- "version": "1.1.19",
5
+ "version": "1.1.20",
6
6
  "description": "Source-of-truth API contract for ServiceAi. Generated from `shared/schema.ts` Zod schemas by `scripts/build-api-spec.ts`. Versioning policy: see `docs/api-versioning.md`. The hand-written docs/ios-contract.md and docs/lidar-mobile-upload-contract.md are human-readable design notes only — this spec is the canonical wire contract.",
7
7
  "license": {
8
8
  "name": "Proprietary",
@@ -114,7 +114,7 @@
114
114
  "properties": {
115
115
  "apiVersion": {
116
116
  "type": "string",
117
- "example": "1.1.19"
117
+ "example": "1.1.20"
118
118
  },
119
119
  "minClientVersion": {
120
120
  "type": "string",
@@ -677,7 +677,8 @@
677
677
  {
678
678
  "type": "null"
679
679
  }
680
- ]
680
+ ],
681
+ "description": "Persisted append-room doorway links on a composite scan. Each link carries both a `sourceOpeningKind` (the appended child scan's entry side — always `door` in practice, since iOS does not capture openings on a single-room append payload) and a `targetOpeningKind` (`door` or `opening`) that controls kind-specific resolution against the parent scan: a link with `targetOpeningKind: \"door\"` indexes into the target room's `doors[]`, a link with `targetOpeningKind: \"opening\"` indexes into the target room's `openings[]`. The two arrays are never folded together. Windows are not traversable and never appear here."
681
682
  },
682
683
  "rawRoomPlanData": {
683
684
  "anyOf": [
@@ -2143,7 +2144,8 @@
2143
2144
  ]
2144
2145
  }
2145
2146
  }
2146
- }
2147
+ },
2148
+ "description": "Mobile-upload scan room. `doors`, `windows`, and `openings` are three distinct non-overlapping arrays — array membership IS the discriminator (there is no `kind` / `type` field on the item itself). Append-room `entryDoorwayId` of the form `scan-<scanId>-room-<roomLocalId>-(door|opening)-<idx>` resolves kind-specifically against the parent scan's matching array only: a `door` segment indexes `doors[]`, an `opening` segment indexes `openings[]`. Windows are not traversable and never appear as an `entryDoorwayId` target."
2147
2149
  }
2148
2150
  }
2149
2151
  },
@@ -4487,6 +4489,213 @@
4487
4489
  "type": "object",
4488
4490
  "additionalProperties": {}
4489
4491
  },
4492
+ {
4493
+ "type": "object",
4494
+ "properties": {
4495
+ "doors": {
4496
+ "type": "array",
4497
+ "items": {
4498
+ "type": "object",
4499
+ "properties": {
4500
+ "position": {
4501
+ "type": "object",
4502
+ "properties": {
4503
+ "x": {
4504
+ "type": "number"
4505
+ },
4506
+ "y": {
4507
+ "type": "number"
4508
+ },
4509
+ "z": {
4510
+ "type": "number"
4511
+ }
4512
+ },
4513
+ "required": [
4514
+ "x",
4515
+ "y",
4516
+ "z"
4517
+ ]
4518
+ },
4519
+ "width": {
4520
+ "type": "number",
4521
+ "exclusiveMinimum": 0
4522
+ },
4523
+ "height": {
4524
+ "type": "number",
4525
+ "exclusiveMinimum": 0
4526
+ },
4527
+ "hostWallIndex": {
4528
+ "type": [
4529
+ "integer",
4530
+ "null"
4531
+ ],
4532
+ "minimum": 0
4533
+ },
4534
+ "wallIndex": {
4535
+ "type": "integer",
4536
+ "minimum": 0
4537
+ },
4538
+ "swingHand": {
4539
+ "type": "string",
4540
+ "enum": [
4541
+ "left",
4542
+ "right",
4543
+ "none"
4544
+ ]
4545
+ },
4546
+ "placementSource": {
4547
+ "type": "string",
4548
+ "enum": [
4549
+ "auto",
4550
+ "manual"
4551
+ ]
4552
+ }
4553
+ },
4554
+ "required": [
4555
+ "position",
4556
+ "width",
4557
+ "height"
4558
+ ]
4559
+ },
4560
+ "description": "RoomPlan-classified doors. Distinct from `windows[]` and `openings[]`; append-room `entryDoorwayId` tokens of the form `…-door-<idx>` resolve into THIS array only."
4561
+ },
4562
+ "windows": {
4563
+ "type": "array",
4564
+ "items": {
4565
+ "type": "object",
4566
+ "properties": {
4567
+ "position": {
4568
+ "type": "object",
4569
+ "properties": {
4570
+ "x": {
4571
+ "type": "number"
4572
+ },
4573
+ "y": {
4574
+ "type": "number"
4575
+ },
4576
+ "z": {
4577
+ "type": "number"
4578
+ }
4579
+ },
4580
+ "required": [
4581
+ "x",
4582
+ "y",
4583
+ "z"
4584
+ ]
4585
+ },
4586
+ "width": {
4587
+ "type": "number",
4588
+ "exclusiveMinimum": 0
4589
+ },
4590
+ "height": {
4591
+ "type": "number",
4592
+ "exclusiveMinimum": 0
4593
+ },
4594
+ "hostWallIndex": {
4595
+ "type": [
4596
+ "integer",
4597
+ "null"
4598
+ ],
4599
+ "minimum": 0
4600
+ },
4601
+ "wallIndex": {
4602
+ "type": "integer",
4603
+ "minimum": 0
4604
+ },
4605
+ "swingHand": {
4606
+ "type": "string",
4607
+ "enum": [
4608
+ "left",
4609
+ "right",
4610
+ "none"
4611
+ ]
4612
+ },
4613
+ "placementSource": {
4614
+ "type": "string",
4615
+ "enum": [
4616
+ "auto",
4617
+ "manual"
4618
+ ]
4619
+ }
4620
+ },
4621
+ "required": [
4622
+ "position",
4623
+ "width",
4624
+ "height"
4625
+ ]
4626
+ },
4627
+ "description": "RoomPlan-classified windows. Distinct from `doors[]` and `openings[]`; no append-room `entryDoorwayId` ever resolves into this array (windows are not traversable)."
4628
+ },
4629
+ "openings": {
4630
+ "type": "array",
4631
+ "items": {
4632
+ "type": "object",
4633
+ "properties": {
4634
+ "position": {
4635
+ "type": "object",
4636
+ "properties": {
4637
+ "x": {
4638
+ "type": "number"
4639
+ },
4640
+ "y": {
4641
+ "type": "number"
4642
+ },
4643
+ "z": {
4644
+ "type": "number"
4645
+ }
4646
+ },
4647
+ "required": [
4648
+ "x",
4649
+ "y",
4650
+ "z"
4651
+ ]
4652
+ },
4653
+ "width": {
4654
+ "type": "number",
4655
+ "exclusiveMinimum": 0
4656
+ },
4657
+ "height": {
4658
+ "type": "number",
4659
+ "exclusiveMinimum": 0
4660
+ },
4661
+ "hostWallIndex": {
4662
+ "type": [
4663
+ "integer",
4664
+ "null"
4665
+ ],
4666
+ "minimum": 0
4667
+ },
4668
+ "wallIndex": {
4669
+ "type": "integer",
4670
+ "minimum": 0
4671
+ },
4672
+ "swingHand": {
4673
+ "type": "string",
4674
+ "enum": [
4675
+ "left",
4676
+ "right",
4677
+ "none"
4678
+ ]
4679
+ },
4680
+ "placementSource": {
4681
+ "type": "string",
4682
+ "enum": [
4683
+ "auto",
4684
+ "manual"
4685
+ ]
4686
+ }
4687
+ },
4688
+ "required": [
4689
+ "position",
4690
+ "width",
4691
+ "height"
4692
+ ]
4693
+ },
4694
+ "description": "RoomPlan-classified archways / passages (Task #730). Distinct from `doors[]` and `windows[]`; append-room `entryDoorwayId` tokens of the form `…-opening-<idx>` resolve into THIS array only — they are never folded into `doors[]`."
4695
+ }
4696
+ },
4697
+ "description": "Scan room read shape. `doors`, `windows`, and `openings` are three distinct non-overlapping arrays — array membership IS the discriminator (there is no `kind` / `type` field on the item itself). Append-room doorway links resolve kind-specifically: a `door` segment never indexes into `openings[]` and vice versa."
4698
+ },
4490
4699
  {
4491
4700
  "type": "object",
4492
4701
  "properties": {
@@ -4657,8 +4866,219 @@
4657
4866
  "rooms": {
4658
4867
  "type": "array",
4659
4868
  "items": {
4660
- "type": "object",
4661
- "additionalProperties": {}
4869
+ "allOf": [
4870
+ {
4871
+ "type": "object",
4872
+ "additionalProperties": {}
4873
+ },
4874
+ {
4875
+ "type": "object",
4876
+ "properties": {
4877
+ "doors": {
4878
+ "type": "array",
4879
+ "items": {
4880
+ "type": "object",
4881
+ "properties": {
4882
+ "position": {
4883
+ "type": "object",
4884
+ "properties": {
4885
+ "x": {
4886
+ "type": "number"
4887
+ },
4888
+ "y": {
4889
+ "type": "number"
4890
+ },
4891
+ "z": {
4892
+ "type": "number"
4893
+ }
4894
+ },
4895
+ "required": [
4896
+ "x",
4897
+ "y",
4898
+ "z"
4899
+ ]
4900
+ },
4901
+ "width": {
4902
+ "type": "number",
4903
+ "exclusiveMinimum": 0
4904
+ },
4905
+ "height": {
4906
+ "type": "number",
4907
+ "exclusiveMinimum": 0
4908
+ },
4909
+ "hostWallIndex": {
4910
+ "type": [
4911
+ "integer",
4912
+ "null"
4913
+ ],
4914
+ "minimum": 0
4915
+ },
4916
+ "wallIndex": {
4917
+ "type": "integer",
4918
+ "minimum": 0
4919
+ },
4920
+ "swingHand": {
4921
+ "type": "string",
4922
+ "enum": [
4923
+ "left",
4924
+ "right",
4925
+ "none"
4926
+ ]
4927
+ },
4928
+ "placementSource": {
4929
+ "type": "string",
4930
+ "enum": [
4931
+ "auto",
4932
+ "manual"
4933
+ ]
4934
+ }
4935
+ },
4936
+ "required": [
4937
+ "position",
4938
+ "width",
4939
+ "height"
4940
+ ]
4941
+ },
4942
+ "description": "RoomPlan-classified doors. Distinct from `windows[]` and `openings[]`; append-room `entryDoorwayId` tokens of the form `…-door-<idx>` resolve into THIS array only."
4943
+ },
4944
+ "windows": {
4945
+ "type": "array",
4946
+ "items": {
4947
+ "type": "object",
4948
+ "properties": {
4949
+ "position": {
4950
+ "type": "object",
4951
+ "properties": {
4952
+ "x": {
4953
+ "type": "number"
4954
+ },
4955
+ "y": {
4956
+ "type": "number"
4957
+ },
4958
+ "z": {
4959
+ "type": "number"
4960
+ }
4961
+ },
4962
+ "required": [
4963
+ "x",
4964
+ "y",
4965
+ "z"
4966
+ ]
4967
+ },
4968
+ "width": {
4969
+ "type": "number",
4970
+ "exclusiveMinimum": 0
4971
+ },
4972
+ "height": {
4973
+ "type": "number",
4974
+ "exclusiveMinimum": 0
4975
+ },
4976
+ "hostWallIndex": {
4977
+ "type": [
4978
+ "integer",
4979
+ "null"
4980
+ ],
4981
+ "minimum": 0
4982
+ },
4983
+ "wallIndex": {
4984
+ "type": "integer",
4985
+ "minimum": 0
4986
+ },
4987
+ "swingHand": {
4988
+ "type": "string",
4989
+ "enum": [
4990
+ "left",
4991
+ "right",
4992
+ "none"
4993
+ ]
4994
+ },
4995
+ "placementSource": {
4996
+ "type": "string",
4997
+ "enum": [
4998
+ "auto",
4999
+ "manual"
5000
+ ]
5001
+ }
5002
+ },
5003
+ "required": [
5004
+ "position",
5005
+ "width",
5006
+ "height"
5007
+ ]
5008
+ },
5009
+ "description": "RoomPlan-classified windows. Distinct from `doors[]` and `openings[]`; no append-room `entryDoorwayId` ever resolves into this array (windows are not traversable)."
5010
+ },
5011
+ "openings": {
5012
+ "type": "array",
5013
+ "items": {
5014
+ "type": "object",
5015
+ "properties": {
5016
+ "position": {
5017
+ "type": "object",
5018
+ "properties": {
5019
+ "x": {
5020
+ "type": "number"
5021
+ },
5022
+ "y": {
5023
+ "type": "number"
5024
+ },
5025
+ "z": {
5026
+ "type": "number"
5027
+ }
5028
+ },
5029
+ "required": [
5030
+ "x",
5031
+ "y",
5032
+ "z"
5033
+ ]
5034
+ },
5035
+ "width": {
5036
+ "type": "number",
5037
+ "exclusiveMinimum": 0
5038
+ },
5039
+ "height": {
5040
+ "type": "number",
5041
+ "exclusiveMinimum": 0
5042
+ },
5043
+ "hostWallIndex": {
5044
+ "type": [
5045
+ "integer",
5046
+ "null"
5047
+ ],
5048
+ "minimum": 0
5049
+ },
5050
+ "wallIndex": {
5051
+ "type": "integer",
5052
+ "minimum": 0
5053
+ },
5054
+ "swingHand": {
5055
+ "type": "string",
5056
+ "enum": [
5057
+ "left",
5058
+ "right",
5059
+ "none"
5060
+ ]
5061
+ },
5062
+ "placementSource": {
5063
+ "type": "string",
5064
+ "enum": [
5065
+ "auto",
5066
+ "manual"
5067
+ ]
5068
+ }
5069
+ },
5070
+ "required": [
5071
+ "position",
5072
+ "width",
5073
+ "height"
5074
+ ]
5075
+ },
5076
+ "description": "RoomPlan-classified archways / passages (Task #730). Distinct from `doors[]` and `windows[]`; append-room `entryDoorwayId` tokens of the form `…-opening-<idx>` resolve into THIS array only — they are never folded into `doors[]`."
5077
+ }
5078
+ },
5079
+ "description": "Scan room read shape. `doors`, `windows`, and `openings` are three distinct non-overlapping arrays — array membership IS the discriminator (there is no `kind` / `type` field on the item itself). Append-room doorway links resolve kind-specifically: a `door` segment never indexes into `openings[]` and vice versa."
5080
+ }
5081
+ ]
4662
5082
  }
4663
5083
  },
4664
5084
  "sessionConnections": {
package/openapi.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  openapi: 3.1.0
2
2
  info:
3
3
  title: ServiceAi API
4
- version: 1.1.19
4
+ version: 1.1.20
5
5
  description: >-
6
6
  Source-of-truth API contract for ServiceAi. Generated from `shared/schema.ts` Zod schemas by
7
7
  `scripts/build-api-spec.ts`. Versioning policy: see `docs/api-versioning.md`. The hand-written docs/ios-contract.md
@@ -78,7 +78,7 @@ components:
78
78
  properties:
79
79
  apiVersion:
80
80
  type: string
81
- example: 1.1.19
81
+ example: 1.1.20
82
82
  minClientVersion:
83
83
  type: string
84
84
  example: 1.0.0
@@ -438,6 +438,13 @@ components:
438
438
  - type: array
439
439
  items: {}
440
440
  - type: 'null'
441
+ description: >-
442
+ Persisted append-room doorway links on a composite scan. Each link carries both a `sourceOpeningKind` (the
443
+ appended child scan's entry side — always `door` in practice, since iOS does not capture openings on a
444
+ single-room append payload) and a `targetOpeningKind` (`door` or `opening`) that controls kind-specific
445
+ resolution against the parent scan: a link with `targetOpeningKind: "door"` indexes into the target room's
446
+ `doors[]`, a link with `targetOpeningKind: "opening"` indexes into the target room's `openings[]`. The two
447
+ arrays are never folded together. Windows are not traversable and never appear here.
441
448
  rawRoomPlanData:
442
449
  anyOf:
443
450
  - type: string
@@ -1361,6 +1368,13 @@ components:
1361
1368
  - position
1362
1369
  - width
1363
1370
  - height
1371
+ description: >-
1372
+ Mobile-upload scan room. `doors`, `windows`, and `openings` are three distinct non-overlapping arrays
1373
+ — array membership IS the discriminator (there is no `kind` / `type` field on the item itself).
1374
+ Append-room `entryDoorwayId` of the form `scan-<scanId>-room-<roomLocalId>-(door|opening)-<idx>`
1375
+ resolves kind-specifically against the parent scan's matching array only: a `door` segment indexes
1376
+ `doors[]`, an `opening` segment indexes `openings[]`. Windows are not traversable and never appear as
1377
+ an `entryDoorwayId` target.
1364
1378
  required:
1365
1379
  - rooms
1366
1380
  name:
@@ -2803,6 +2817,149 @@ paths:
2803
2817
  allOf:
2804
2818
  - type: object
2805
2819
  additionalProperties: {}
2820
+ - type: object
2821
+ properties:
2822
+ doors:
2823
+ type: array
2824
+ items:
2825
+ type: object
2826
+ properties:
2827
+ position:
2828
+ type: object
2829
+ properties:
2830
+ x:
2831
+ type: number
2832
+ 'y':
2833
+ type: number
2834
+ z:
2835
+ type: number
2836
+ required:
2837
+ - x
2838
+ - 'y'
2839
+ - z
2840
+ width:
2841
+ type: number
2842
+ exclusiveMinimum: 0
2843
+ height:
2844
+ type: number
2845
+ exclusiveMinimum: 0
2846
+ hostWallIndex:
2847
+ type:
2848
+ - integer
2849
+ - 'null'
2850
+ minimum: 0
2851
+ wallIndex:
2852
+ type: integer
2853
+ minimum: 0
2854
+ swingHand:
2855
+ type: string
2856
+ enum: *ref_0
2857
+ placementSource:
2858
+ type: string
2859
+ enum: *ref_1
2860
+ required:
2861
+ - position
2862
+ - width
2863
+ - height
2864
+ description: >-
2865
+ RoomPlan-classified doors. Distinct from `windows[]` and `openings[]`; append-room
2866
+ `entryDoorwayId` tokens of the form `…-door-<idx>` resolve into THIS array only.
2867
+ windows:
2868
+ type: array
2869
+ items:
2870
+ type: object
2871
+ properties:
2872
+ position:
2873
+ type: object
2874
+ properties:
2875
+ x:
2876
+ type: number
2877
+ 'y':
2878
+ type: number
2879
+ z:
2880
+ type: number
2881
+ required:
2882
+ - x
2883
+ - 'y'
2884
+ - z
2885
+ width:
2886
+ type: number
2887
+ exclusiveMinimum: 0
2888
+ height:
2889
+ type: number
2890
+ exclusiveMinimum: 0
2891
+ hostWallIndex:
2892
+ type:
2893
+ - integer
2894
+ - 'null'
2895
+ minimum: 0
2896
+ wallIndex:
2897
+ type: integer
2898
+ minimum: 0
2899
+ swingHand:
2900
+ type: string
2901
+ enum: *ref_0
2902
+ placementSource:
2903
+ type: string
2904
+ enum: *ref_1
2905
+ required:
2906
+ - position
2907
+ - width
2908
+ - height
2909
+ description: >-
2910
+ RoomPlan-classified windows. Distinct from `doors[]` and `openings[]`; no append-room
2911
+ `entryDoorwayId` ever resolves into this array (windows are not traversable).
2912
+ openings:
2913
+ type: array
2914
+ items:
2915
+ type: object
2916
+ properties:
2917
+ position:
2918
+ type: object
2919
+ properties:
2920
+ x:
2921
+ type: number
2922
+ 'y':
2923
+ type: number
2924
+ z:
2925
+ type: number
2926
+ required:
2927
+ - x
2928
+ - 'y'
2929
+ - z
2930
+ width:
2931
+ type: number
2932
+ exclusiveMinimum: 0
2933
+ height:
2934
+ type: number
2935
+ exclusiveMinimum: 0
2936
+ hostWallIndex:
2937
+ type:
2938
+ - integer
2939
+ - 'null'
2940
+ minimum: 0
2941
+ wallIndex:
2942
+ type: integer
2943
+ minimum: 0
2944
+ swingHand:
2945
+ type: string
2946
+ enum: *ref_0
2947
+ placementSource:
2948
+ type: string
2949
+ enum: *ref_1
2950
+ required:
2951
+ - position
2952
+ - width
2953
+ - height
2954
+ description: >-
2955
+ RoomPlan-classified archways / passages (Task #730). Distinct from `doors[]` and
2956
+ `windows[]`; append-room `entryDoorwayId` tokens of the form `…-opening-<idx>` resolve into
2957
+ THIS array only — they are never folded into `doors[]`.
2958
+ description: >-
2959
+ Scan room read shape. `doors`, `windows`, and `openings` are three distinct non-overlapping
2960
+ arrays — array membership IS the discriminator (there is no `kind` / `type` field on the item
2961
+ itself). Append-room doorway links resolve kind-specifically: a `door` segment never indexes
2962
+ into `openings[]` and vice versa.
2806
2963
  - type: object
2807
2964
  properties:
2808
2965
  floorPlanFrame:
@@ -2907,8 +3064,152 @@ paths:
2907
3064
  rooms:
2908
3065
  type: array
2909
3066
  items:
2910
- type: object
2911
- additionalProperties: {}
3067
+ allOf:
3068
+ - type: object
3069
+ additionalProperties: {}
3070
+ - type: object
3071
+ properties:
3072
+ doors:
3073
+ type: array
3074
+ items:
3075
+ type: object
3076
+ properties:
3077
+ position:
3078
+ type: object
3079
+ properties:
3080
+ x:
3081
+ type: number
3082
+ 'y':
3083
+ type: number
3084
+ z:
3085
+ type: number
3086
+ required:
3087
+ - x
3088
+ - 'y'
3089
+ - z
3090
+ width:
3091
+ type: number
3092
+ exclusiveMinimum: 0
3093
+ height:
3094
+ type: number
3095
+ exclusiveMinimum: 0
3096
+ hostWallIndex:
3097
+ type:
3098
+ - integer
3099
+ - 'null'
3100
+ minimum: 0
3101
+ wallIndex:
3102
+ type: integer
3103
+ minimum: 0
3104
+ swingHand:
3105
+ type: string
3106
+ enum: *ref_0
3107
+ placementSource:
3108
+ type: string
3109
+ enum: *ref_1
3110
+ required:
3111
+ - position
3112
+ - width
3113
+ - height
3114
+ description: >-
3115
+ RoomPlan-classified doors. Distinct from `windows[]` and `openings[]`; append-room
3116
+ `entryDoorwayId` tokens of the form `…-door-<idx>` resolve into THIS array only.
3117
+ windows:
3118
+ type: array
3119
+ items:
3120
+ type: object
3121
+ properties:
3122
+ position:
3123
+ type: object
3124
+ properties:
3125
+ x:
3126
+ type: number
3127
+ 'y':
3128
+ type: number
3129
+ z:
3130
+ type: number
3131
+ required:
3132
+ - x
3133
+ - 'y'
3134
+ - z
3135
+ width:
3136
+ type: number
3137
+ exclusiveMinimum: 0
3138
+ height:
3139
+ type: number
3140
+ exclusiveMinimum: 0
3141
+ hostWallIndex:
3142
+ type:
3143
+ - integer
3144
+ - 'null'
3145
+ minimum: 0
3146
+ wallIndex:
3147
+ type: integer
3148
+ minimum: 0
3149
+ swingHand:
3150
+ type: string
3151
+ enum: *ref_0
3152
+ placementSource:
3153
+ type: string
3154
+ enum: *ref_1
3155
+ required:
3156
+ - position
3157
+ - width
3158
+ - height
3159
+ description: >-
3160
+ RoomPlan-classified windows. Distinct from `doors[]` and `openings[]`; no append-room
3161
+ `entryDoorwayId` ever resolves into this array (windows are not traversable).
3162
+ openings:
3163
+ type: array
3164
+ items:
3165
+ type: object
3166
+ properties:
3167
+ position:
3168
+ type: object
3169
+ properties:
3170
+ x:
3171
+ type: number
3172
+ 'y':
3173
+ type: number
3174
+ z:
3175
+ type: number
3176
+ required:
3177
+ - x
3178
+ - 'y'
3179
+ - z
3180
+ width:
3181
+ type: number
3182
+ exclusiveMinimum: 0
3183
+ height:
3184
+ type: number
3185
+ exclusiveMinimum: 0
3186
+ hostWallIndex:
3187
+ type:
3188
+ - integer
3189
+ - 'null'
3190
+ minimum: 0
3191
+ wallIndex:
3192
+ type: integer
3193
+ minimum: 0
3194
+ swingHand:
3195
+ type: string
3196
+ enum: *ref_0
3197
+ placementSource:
3198
+ type: string
3199
+ enum: *ref_1
3200
+ required:
3201
+ - position
3202
+ - width
3203
+ - height
3204
+ description: >-
3205
+ RoomPlan-classified archways / passages (Task #730). Distinct from `doors[]` and
3206
+ `windows[]`; append-room `entryDoorwayId` tokens of the form `…-opening-<idx>` resolve
3207
+ into THIS array only — they are never folded into `doors[]`.
3208
+ description: >-
3209
+ Scan room read shape. `doors`, `windows`, and `openings` are three distinct non-overlapping
3210
+ arrays — array membership IS the discriminator (there is no `kind` / `type` field on the
3211
+ item itself). Append-room doorway links resolve kind-specifically: a `door` segment never
3212
+ indexes into `openings[]` and vice versa.
2912
3213
  sessionConnections:
2913
3214
  type: array
2914
3215
  items:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@serviceai/api-spec",
3
- "version": "1.1.19",
3
+ "version": "1.1.20",
4
4
  "description": "ServiceAi OpenAPI 3.1 contract + generated TS types. Auto-published from the desktop repo.",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
package/types.d.ts CHANGED
@@ -1058,6 +1058,55 @@ export interface paths {
1058
1058
  content: {
1059
1059
  "application/json": ({
1060
1060
  [key: string]: unknown;
1061
+ } & {
1062
+ /** @description RoomPlan-classified doors. Distinct from `windows[]` and `openings[]`; append-room `entryDoorwayId` tokens of the form `…-door-<idx>` resolve into THIS array only. */
1063
+ doors?: {
1064
+ position: {
1065
+ x: number;
1066
+ y: number;
1067
+ z: number;
1068
+ };
1069
+ width: number;
1070
+ height: number;
1071
+ hostWallIndex?: number | null;
1072
+ wallIndex?: number;
1073
+ /** @enum {string} */
1074
+ swingHand?: "left" | "right" | "none";
1075
+ /** @enum {string} */
1076
+ placementSource?: "auto" | "manual";
1077
+ }[];
1078
+ /** @description RoomPlan-classified windows. Distinct from `doors[]` and `openings[]`; no append-room `entryDoorwayId` ever resolves into this array (windows are not traversable). */
1079
+ windows?: {
1080
+ position: {
1081
+ x: number;
1082
+ y: number;
1083
+ z: number;
1084
+ };
1085
+ width: number;
1086
+ height: number;
1087
+ hostWallIndex?: number | null;
1088
+ wallIndex?: number;
1089
+ /** @enum {string} */
1090
+ swingHand?: "left" | "right" | "none";
1091
+ /** @enum {string} */
1092
+ placementSource?: "auto" | "manual";
1093
+ }[];
1094
+ /** @description RoomPlan-classified archways / passages (Task #730). Distinct from `doors[]` and `windows[]`; append-room `entryDoorwayId` tokens of the form `…-opening-<idx>` resolve into THIS array only — they are never folded into `doors[]`. */
1095
+ openings?: {
1096
+ position: {
1097
+ x: number;
1098
+ y: number;
1099
+ z: number;
1100
+ };
1101
+ width: number;
1102
+ height: number;
1103
+ hostWallIndex?: number | null;
1104
+ wallIndex?: number;
1105
+ /** @enum {string} */
1106
+ swingHand?: "left" | "right" | "none";
1107
+ /** @enum {string} */
1108
+ placementSource?: "auto" | "manual";
1109
+ }[];
1061
1110
  } & {
1062
1111
  floorPlanFrame: {
1063
1112
  pixelsPerMeter: number;
@@ -1141,9 +1190,58 @@ export interface paths {
1141
1190
  mirrorX: boolean;
1142
1191
  } | null;
1143
1192
  })[];
1144
- rooms: {
1193
+ rooms: ({
1145
1194
  [key: string]: unknown;
1146
- }[];
1195
+ } & {
1196
+ /** @description RoomPlan-classified doors. Distinct from `windows[]` and `openings[]`; append-room `entryDoorwayId` tokens of the form `…-door-<idx>` resolve into THIS array only. */
1197
+ doors?: {
1198
+ position: {
1199
+ x: number;
1200
+ y: number;
1201
+ z: number;
1202
+ };
1203
+ width: number;
1204
+ height: number;
1205
+ hostWallIndex?: number | null;
1206
+ wallIndex?: number;
1207
+ /** @enum {string} */
1208
+ swingHand?: "left" | "right" | "none";
1209
+ /** @enum {string} */
1210
+ placementSource?: "auto" | "manual";
1211
+ }[];
1212
+ /** @description RoomPlan-classified windows. Distinct from `doors[]` and `openings[]`; no append-room `entryDoorwayId` ever resolves into this array (windows are not traversable). */
1213
+ windows?: {
1214
+ position: {
1215
+ x: number;
1216
+ y: number;
1217
+ z: number;
1218
+ };
1219
+ width: number;
1220
+ height: number;
1221
+ hostWallIndex?: number | null;
1222
+ wallIndex?: number;
1223
+ /** @enum {string} */
1224
+ swingHand?: "left" | "right" | "none";
1225
+ /** @enum {string} */
1226
+ placementSource?: "auto" | "manual";
1227
+ }[];
1228
+ /** @description RoomPlan-classified archways / passages (Task #730). Distinct from `doors[]` and `windows[]`; append-room `entryDoorwayId` tokens of the form `…-opening-<idx>` resolve into THIS array only — they are never folded into `doors[]`. */
1229
+ openings?: {
1230
+ position: {
1231
+ x: number;
1232
+ y: number;
1233
+ z: number;
1234
+ };
1235
+ width: number;
1236
+ height: number;
1237
+ hostWallIndex?: number | null;
1238
+ wallIndex?: number;
1239
+ /** @enum {string} */
1240
+ swingHand?: "left" | "right" | "none";
1241
+ /** @enum {string} */
1242
+ placementSource?: "auto" | "manual";
1243
+ }[];
1244
+ })[];
1147
1245
  sessionConnections: {
1148
1246
  sessionId: string;
1149
1247
  scanId: number;
@@ -1204,7 +1302,7 @@ export interface components {
1204
1302
  };
1205
1303
  /** @description Cross-platform version handshake manifest. See docs/api-versioning.md. */
1206
1304
  VersionManifest: {
1207
- /** @example 1.1.19 */
1305
+ /** @example 1.1.20 */
1208
1306
  apiVersion: string;
1209
1307
  /** @example 1.0.0 */
1210
1308
  minClientVersion: string;
@@ -1299,6 +1397,7 @@ export interface components {
1299
1397
  [key: string]: unknown;
1300
1398
  } | unknown[];
1301
1399
  parentScanId?: number | null;
1400
+ /** @description Persisted append-room doorway links on a composite scan. Each link carries both a `sourceOpeningKind` (the appended child scan's entry side — always `door` in practice, since iOS does not capture openings on a single-room append payload) and a `targetOpeningKind` (`door` or `opening`) that controls kind-specific resolution against the parent scan: a link with `targetOpeningKind: "door"` indexes into the target room's `doors[]`, a link with `targetOpeningKind: "opening"` indexes into the target room's `openings[]`. The two arrays are never folded together. Windows are not traversable and never appear here. */
1302
1401
  doorwayLinks?: string | number | boolean | null | {
1303
1402
  [key: string]: unknown;
1304
1403
  } | unknown[];