@happyvertical/smrt-projects 0.57.18 → 0.57.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 (57) hide show
  1. package/AGENTS.md +3 -0
  2. package/README.md +68 -0
  3. package/dist/collections/DevelopmentRequests.d.ts.map +1 -1
  4. package/dist/collections/ProjectDecisions.d.ts +9 -0
  5. package/dist/collections/ProjectDecisions.d.ts.map +1 -0
  6. package/dist/collections/ProjectGovernanceEvents.d.ts +11 -0
  7. package/dist/collections/ProjectGovernanceEvents.d.ts.map +1 -0
  8. package/dist/collections/ProjectQuestions.d.ts +9 -0
  9. package/dist/collections/ProjectQuestions.d.ts.map +1 -0
  10. package/dist/collections/ProjectSpecifications.d.ts +8 -0
  11. package/dist/collections/ProjectSpecifications.d.ts.map +1 -0
  12. package/dist/collections/index.d.ts +4 -0
  13. package/dist/collections/index.d.ts.map +1 -1
  14. package/dist/index.d.ts +11 -0
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +1120 -51
  17. package/dist/index.js.map +1 -1
  18. package/dist/manifest.json +4604 -2323
  19. package/dist/models/DevelopmentRequest.d.ts +2 -2
  20. package/dist/models/DevelopmentRequest.d.ts.map +1 -1
  21. package/dist/models/ProjectDecision.d.ts +39 -0
  22. package/dist/models/ProjectDecision.d.ts.map +1 -0
  23. package/dist/models/ProjectGovernanceEvent.d.ts +40 -0
  24. package/dist/models/ProjectGovernanceEvent.d.ts.map +1 -0
  25. package/dist/models/ProjectQuestion.d.ts +32 -0
  26. package/dist/models/ProjectQuestion.d.ts.map +1 -0
  27. package/dist/models/ProjectSpecification.d.ts +26 -0
  28. package/dist/models/ProjectSpecification.d.ts.map +1 -0
  29. package/dist/models/index.d.ts +4 -0
  30. package/dist/models/index.d.ts.map +1 -1
  31. package/dist/models/project-governance-validation.d.ts +6 -0
  32. package/dist/models/project-governance-validation.d.ts.map +1 -0
  33. package/dist/project-governance-types.d.ts +49 -0
  34. package/dist/project-governance-types.d.ts.map +1 -0
  35. package/dist/services/development-request-service.d.ts.map +1 -1
  36. package/dist/services/index.d.ts +3 -0
  37. package/dist/services/index.d.ts.map +1 -1
  38. package/dist/services/participant-development-request-service.d.ts +34 -0
  39. package/dist/services/participant-development-request-service.d.ts.map +1 -0
  40. package/dist/services/project-governance-service.d.ts +106 -0
  41. package/dist/services/project-governance-service.d.ts.map +1 -0
  42. package/dist/services/project-specification-service.d.ts +30 -0
  43. package/dist/services/project-specification-service.d.ts.map +1 -0
  44. package/dist/smrt-knowledge.json +915 -49
  45. package/dist/svelte/ProjectQuestionsAndDecisions.svelte +183 -0
  46. package/dist/svelte/ProjectQuestionsAndDecisions.svelte.d.ts +17 -0
  47. package/dist/svelte/ProjectQuestionsAndDecisions.svelte.d.ts.map +1 -0
  48. package/dist/svelte/i18n.d.ts +16 -0
  49. package/dist/svelte/i18n.d.ts.map +1 -1
  50. package/dist/svelte/i18n.js +16 -0
  51. package/dist/svelte/index.d.ts +4 -1
  52. package/dist/svelte/index.d.ts.map +1 -1
  53. package/dist/svelte/index.js +3 -1
  54. package/dist/svelte/project-governance-types.d.ts +35 -0
  55. package/dist/svelte/project-governance-types.d.ts.map +1 -0
  56. package/dist/svelte/project-governance-types.js +1 -0
  57. package/package.json +20 -19
@@ -3,13 +3,13 @@
3
3
  "sensitiveFieldsExcluded": true,
4
4
  "generatedAt": "1970-01-01T00:00:00.000Z",
5
5
  "packageName": "@happyvertical/smrt-projects",
6
- "packageVersion": "0.57.18",
6
+ "packageVersion": "0.57.20",
7
7
  "sourceManifestPath": "dist/manifest.json",
8
8
  "agentDocPath": "AGENTS.md",
9
9
  "sourceHashes": {
10
- "manifest": "ed54dfcc4f478d14ef4e588174a145b2cf21d283bf3047ddbd2ae55813a5133c",
11
- "packageJson": "26b49102140a468173da8a702a8056732f2b07fb0e72d561b062794f8bdc93b1",
12
- "agents": "014098eac2bb1699b653ccd3029e062a2312a521862e39225dd8ca73b6ae8206"
10
+ "manifest": "bd228c7fb50de4a0cad46486ab8d743676edd269f9d1192df6de113f9e303ca4",
11
+ "packageJson": "b277c7069fbbc73e8db9d8b2dcd2951110358bd80c613a521b2d92ea5ab95950",
12
+ "agents": "2879830c12902195bd4f887cf00bdabbe8e7216ebafe2a9b2d007a00d137435c"
13
13
  },
14
14
  "exports": [
15
15
  ".",
@@ -79,6 +79,7 @@
79
79
  "smrt"
80
80
  ],
81
81
  "risks": [
82
+ "polymorphic-associations:4",
82
83
  "sensitive-fields-excluded",
83
84
  "sti-inheritance"
84
85
  ],
@@ -460,6 +461,101 @@
460
461
  "tags": [],
461
462
  "risks": []
462
463
  },
464
+ {
465
+ "name": "ProjectDecisionCollection",
466
+ "qualifiedName": "@happyvertical/smrt-projects:ProjectDecisionCollection",
467
+ "collection": "projectdecisions",
468
+ "tableName": "project_decisions",
469
+ "packageName": "@happyvertical/smrt-projects",
470
+ "extends": "SmrtCollection",
471
+ "fields": [],
472
+ "relationships": [],
473
+ "methods": [
474
+ "getForProject",
475
+ "listForProject"
476
+ ],
477
+ "methodSignatures": [
478
+ {
479
+ "name": "getForProject",
480
+ "async": true,
481
+ "params": [
482
+ "scope: ProjectGovernanceScope",
483
+ "id: string"
484
+ ],
485
+ "returns": "Promise<ProjectDecision | null>"
486
+ },
487
+ {
488
+ "name": "listForProject",
489
+ "async": true,
490
+ "params": [
491
+ "scope: ProjectGovernanceScope",
492
+ "status?: ProjectDecisionStatus",
493
+ "visibility?: ProjectGovernanceVisibility"
494
+ ],
495
+ "returns": "Promise<ProjectDecision[]>"
496
+ }
497
+ ],
498
+ "surfaces": [],
499
+ "withheldSurfaces": [
500
+ {
501
+ "kind": "api",
502
+ "operation": "getForProject",
503
+ "code": "api-disabled",
504
+ "reason": "api is disabled",
505
+ "objectName": "@happyvertical/smrt-projects:ProjectDecisionCollection"
506
+ },
507
+ {
508
+ "kind": "api",
509
+ "operation": "listForProject",
510
+ "code": "api-disabled",
511
+ "reason": "api is disabled",
512
+ "objectName": "@happyvertical/smrt-projects:ProjectDecisionCollection"
513
+ }
514
+ ],
515
+ "relationshipFeatures": [
516
+ "uuidColumns"
517
+ ],
518
+ "tags": [],
519
+ "risks": []
520
+ },
521
+ {
522
+ "name": "ProjectGovernanceEventCollection",
523
+ "qualifiedName": "@happyvertical/smrt-projects:ProjectGovernanceEventCollection",
524
+ "collection": "projectgovernanceevents",
525
+ "tableName": "project_governance_events",
526
+ "packageName": "@happyvertical/smrt-projects",
527
+ "extends": "SmrtCollection",
528
+ "fields": [],
529
+ "relationships": [],
530
+ "methods": [
531
+ "listForRecord"
532
+ ],
533
+ "methodSignatures": [
534
+ {
535
+ "name": "listForRecord",
536
+ "async": true,
537
+ "params": [
538
+ "input: object"
539
+ ],
540
+ "returns": "Promise<ProjectGovernanceEvent[]>"
541
+ }
542
+ ],
543
+ "surfaces": [],
544
+ "withheldSurfaces": [
545
+ {
546
+ "kind": "api",
547
+ "operation": "listForRecord",
548
+ "code": "api-disabled",
549
+ "reason": "api is disabled",
550
+ "objectName": "@happyvertical/smrt-projects:ProjectGovernanceEventCollection"
551
+ }
552
+ ],
553
+ "relationshipFeatures": [
554
+ "uuidColumns"
555
+ ],
556
+ "tags": [],
557
+ "risks": []
558
+ },
463
559
  {
464
560
  "name": "ProjectIntegrationAuditCollection",
465
561
  "qualifiedName": "@happyvertical/smrt-projects:ProjectIntegrationAuditCollection",
@@ -621,6 +717,101 @@
621
717
  "tags": [],
622
718
  "risks": []
623
719
  },
720
+ {
721
+ "name": "ProjectQuestionCollection",
722
+ "qualifiedName": "@happyvertical/smrt-projects:ProjectQuestionCollection",
723
+ "collection": "projectquestions",
724
+ "tableName": "project_questions",
725
+ "packageName": "@happyvertical/smrt-projects",
726
+ "extends": "SmrtCollection",
727
+ "fields": [],
728
+ "relationships": [],
729
+ "methods": [
730
+ "getForProject",
731
+ "listForProject"
732
+ ],
733
+ "methodSignatures": [
734
+ {
735
+ "name": "getForProject",
736
+ "async": true,
737
+ "params": [
738
+ "scope: ProjectGovernanceScope",
739
+ "id: string"
740
+ ],
741
+ "returns": "Promise<ProjectQuestion | null>"
742
+ },
743
+ {
744
+ "name": "listForProject",
745
+ "async": true,
746
+ "params": [
747
+ "scope: ProjectGovernanceScope",
748
+ "status?: ProjectQuestionStatus",
749
+ "visibility?: ProjectGovernanceVisibility"
750
+ ],
751
+ "returns": "Promise<ProjectQuestion[]>"
752
+ }
753
+ ],
754
+ "surfaces": [],
755
+ "withheldSurfaces": [
756
+ {
757
+ "kind": "api",
758
+ "operation": "getForProject",
759
+ "code": "api-disabled",
760
+ "reason": "api is disabled",
761
+ "objectName": "@happyvertical/smrt-projects:ProjectQuestionCollection"
762
+ },
763
+ {
764
+ "kind": "api",
765
+ "operation": "listForProject",
766
+ "code": "api-disabled",
767
+ "reason": "api is disabled",
768
+ "objectName": "@happyvertical/smrt-projects:ProjectQuestionCollection"
769
+ }
770
+ ],
771
+ "relationshipFeatures": [
772
+ "uuidColumns"
773
+ ],
774
+ "tags": [],
775
+ "risks": []
776
+ },
777
+ {
778
+ "name": "ProjectSpecificationCollection",
779
+ "qualifiedName": "@happyvertical/smrt-projects:ProjectSpecificationCollection",
780
+ "collection": "projectspecifications",
781
+ "tableName": "project_specifications",
782
+ "packageName": "@happyvertical/smrt-projects",
783
+ "extends": "SmrtCollection",
784
+ "fields": [],
785
+ "relationships": [],
786
+ "methods": [
787
+ "getForProject"
788
+ ],
789
+ "methodSignatures": [
790
+ {
791
+ "name": "getForProject",
792
+ "async": true,
793
+ "params": [
794
+ "scope: ProjectGovernanceScope"
795
+ ],
796
+ "returns": "Promise<ProjectSpecification | null>"
797
+ }
798
+ ],
799
+ "surfaces": [],
800
+ "withheldSurfaces": [
801
+ {
802
+ "kind": "api",
803
+ "operation": "getForProject",
804
+ "code": "api-disabled",
805
+ "reason": "api is disabled",
806
+ "objectName": "@happyvertical/smrt-projects:ProjectSpecificationCollection"
807
+ }
808
+ ],
809
+ "relationshipFeatures": [
810
+ "uuidColumns"
811
+ ],
812
+ "tags": [],
813
+ "risks": []
814
+ },
624
815
  {
625
816
  "name": "ProjectCollection",
626
817
  "qualifiedName": "@happyvertical/smrt-projects:ProjectCollection",
@@ -1436,7 +1627,7 @@
1436
1627
  {
1437
1628
  "name": "integrationId",
1438
1629
  "type": "foreignKey",
1439
- "required": true,
1630
+ "required": false,
1440
1631
  "related": "ProjectIntegration",
1441
1632
  "columnType": "UUID"
1442
1633
  },
@@ -1508,7 +1699,7 @@
1508
1699
  {
1509
1700
  "name": "integrationId",
1510
1701
  "type": "foreignKey",
1511
- "required": true,
1702
+ "required": false,
1512
1703
  "related": "ProjectIntegration",
1513
1704
  "columnType": "UUID"
1514
1705
  }
@@ -2904,14 +3095,38 @@
2904
3095
  "risks": []
2905
3096
  },
2906
3097
  {
2907
- "name": "ProjectIntegration",
2908
- "qualifiedName": "@happyvertical/smrt-projects:ProjectIntegration",
2909
- "displayLabelField": "name",
2910
- "collection": "projectintegrations",
2911
- "tableName": "project_integrations",
3098
+ "name": "ProjectDecision",
3099
+ "qualifiedName": "@happyvertical/smrt-projects:ProjectDecision",
3100
+ "collection": "projectdecisions",
3101
+ "tableName": "project_decisions",
2912
3102
  "packageName": "@happyvertical/smrt-projects",
2913
- "extends": "SmrtObject",
3103
+ "extends": "SmrtPolymorphicAssociation",
2914
3104
  "fields": [
3105
+ {
3106
+ "name": "metaType",
3107
+ "type": "text",
3108
+ "required": true,
3109
+ "columnType": "TEXT"
3110
+ },
3111
+ {
3112
+ "name": "metaId",
3113
+ "type": "text",
3114
+ "required": true,
3115
+ "columnType": "TEXT"
3116
+ },
3117
+ {
3118
+ "name": "role",
3119
+ "type": "text",
3120
+ "required": true,
3121
+ "columnType": "TEXT"
3122
+ },
3123
+ {
3124
+ "name": "sortOrder",
3125
+ "type": "integer",
3126
+ "required": false,
3127
+ "columnType": "INTEGER",
3128
+ "default": 0
3129
+ },
2915
3130
  {
2916
3131
  "name": "tenantId",
2917
3132
  "type": "text",
@@ -2919,35 +3134,71 @@
2919
3134
  "columnType": "UUID"
2920
3135
  },
2921
3136
  {
2922
- "name": "projectId",
3137
+ "name": "question",
3138
+ "type": "text",
3139
+ "required": true,
3140
+ "columnType": "TEXT"
3141
+ },
3142
+ {
3143
+ "name": "decisionOptions",
3144
+ "type": "text",
3145
+ "required": false,
3146
+ "columnType": "TEXT"
3147
+ },
3148
+ {
3149
+ "name": "outcome",
3150
+ "type": "text",
3151
+ "required": false,
3152
+ "columnType": "TEXT"
3153
+ },
3154
+ {
3155
+ "name": "rationale",
2923
3156
  "type": "text",
2924
3157
  "required": false,
3158
+ "columnType": "TEXT"
3159
+ },
3160
+ {
3161
+ "name": "status",
3162
+ "type": "text",
3163
+ "required": true,
2925
3164
  "columnType": "TEXT",
2926
- "default": ""
3165
+ "default": "open"
2927
3166
  },
2928
3167
  {
2929
- "name": "name",
3168
+ "name": "ownerType",
2930
3169
  "type": "text",
2931
3170
  "required": false,
2932
3171
  "columnType": "TEXT",
2933
3172
  "default": ""
2934
3173
  },
2935
3174
  {
2936
- "name": "capabilities",
2937
- "type": "json",
3175
+ "name": "ownerId",
3176
+ "type": "text",
2938
3177
  "required": false,
2939
- "columnType": "JSON",
2940
- "default": []
3178
+ "columnType": "TEXT"
2941
3179
  },
2942
3180
  {
2943
- "name": "status",
3181
+ "name": "visibility",
2944
3182
  "type": "text",
2945
3183
  "required": false,
2946
3184
  "columnType": "TEXT",
2947
- "default": "active"
3185
+ "default": "internal"
2948
3186
  },
2949
3187
  {
2950
- "name": "revokedAt",
3188
+ "name": "contextReferences",
3189
+ "type": "text",
3190
+ "required": false,
3191
+ "columnType": "TEXT"
3192
+ },
3193
+ {
3194
+ "name": "revision",
3195
+ "type": "integer",
3196
+ "required": true,
3197
+ "columnType": "INTEGER",
3198
+ "default": 1
3199
+ },
3200
+ {
3201
+ "name": "decidedAt",
2951
3202
  "type": "datetime",
2952
3203
  "required": false,
2953
3204
  "columnType": "TIMESTAMP"
@@ -2955,20 +3206,39 @@
2955
3206
  ],
2956
3207
  "relationships": [],
2957
3208
  "methods": [
2958
- "hasCapability",
2959
- "isActive"
3209
+ "getContextReferences",
3210
+ "getOptions",
3211
+ "hydrate",
3212
+ "setContextReferences",
3213
+ "setOptions"
2960
3214
  ],
2961
3215
  "methodSignatures": [
2962
3216
  {
2963
- "name": "hasCapability",
3217
+ "name": "getContextReferences",
3218
+ "returns": "ProjectContextReference[]"
3219
+ },
3220
+ {
3221
+ "name": "getOptions",
3222
+ "returns": "ProjectDecisionOption[]"
3223
+ },
3224
+ {
3225
+ "name": "hydrate",
3226
+ "async": true,
3227
+ "returns": "Promise<T | null>"
3228
+ },
3229
+ {
3230
+ "name": "setContextReferences",
2964
3231
  "params": [
2965
- "capability: ProjectIntegrationCapability"
3232
+ "value: ProjectContextReference[]"
2966
3233
  ],
2967
- "returns": "boolean"
3234
+ "returns": "void"
2968
3235
  },
2969
3236
  {
2970
- "name": "isActive",
2971
- "returns": "boolean"
3237
+ "name": "setOptions",
3238
+ "params": [
3239
+ "value: ProjectDecisionOption[]"
3240
+ ],
3241
+ "returns": "void"
2972
3242
  }
2973
3243
  ],
2974
3244
  "tenant": {
@@ -2978,31 +3248,335 @@
2978
3248
  },
2979
3249
  "conflictColumns": [
2980
3250
  "tenant_id",
2981
- "project_id",
2982
- "name"
3251
+ "slug",
3252
+ "context"
2983
3253
  ],
2984
- "surfaces": [
3254
+ "surfaces": [],
3255
+ "withheldSurfaces": [
2985
3256
  {
2986
3257
  "kind": "api",
2987
- "name": "projectintegrations.list",
2988
- "operation": "list",
2989
- "objectName": "@happyvertical/smrt-projects:ProjectIntegration",
2990
- "path": "/projectintegrations",
2991
- "method": "GET"
3258
+ "operation": "getContextReferences",
3259
+ "code": "api-disabled",
3260
+ "reason": "api is disabled",
3261
+ "objectName": "@happyvertical/smrt-projects:ProjectDecision"
2992
3262
  },
2993
3263
  {
2994
3264
  "kind": "api",
2995
- "name": "projectintegrations.get",
2996
- "operation": "get",
2997
- "objectName": "@happyvertical/smrt-projects:ProjectIntegration",
2998
- "path": "/projectintegrations/[id]",
2999
- "method": "GET"
3265
+ "operation": "getOptions",
3266
+ "code": "api-disabled",
3267
+ "reason": "api is disabled",
3268
+ "objectName": "@happyvertical/smrt-projects:ProjectDecision"
3000
3269
  },
3001
3270
  {
3002
- "kind": "cli",
3003
- "name": "projectintegration_list",
3004
- "operation": "list",
3005
- "objectName": "@happyvertical/smrt-projects:ProjectIntegration"
3271
+ "kind": "api",
3272
+ "operation": "hydrate",
3273
+ "code": "api-disabled",
3274
+ "reason": "api is disabled",
3275
+ "objectName": "@happyvertical/smrt-projects:ProjectDecision"
3276
+ },
3277
+ {
3278
+ "kind": "api",
3279
+ "operation": "setContextReferences",
3280
+ "code": "api-disabled",
3281
+ "reason": "api is disabled",
3282
+ "objectName": "@happyvertical/smrt-projects:ProjectDecision"
3283
+ },
3284
+ {
3285
+ "kind": "api",
3286
+ "operation": "setOptions",
3287
+ "code": "api-disabled",
3288
+ "reason": "api is disabled",
3289
+ "objectName": "@happyvertical/smrt-projects:ProjectDecision"
3290
+ }
3291
+ ],
3292
+ "relationshipFeatures": [
3293
+ "SmrtPolymorphicAssociation",
3294
+ "uuidColumns"
3295
+ ],
3296
+ "tags": [],
3297
+ "risks": []
3298
+ },
3299
+ {
3300
+ "name": "ProjectGovernanceEvent",
3301
+ "qualifiedName": "@happyvertical/smrt-projects:ProjectGovernanceEvent",
3302
+ "collection": "projectgovernanceevents",
3303
+ "tableName": "project_governance_events",
3304
+ "packageName": "@happyvertical/smrt-projects",
3305
+ "extends": "SmrtPolymorphicAssociation",
3306
+ "fields": [
3307
+ {
3308
+ "name": "metaType",
3309
+ "type": "text",
3310
+ "required": true,
3311
+ "columnType": "TEXT"
3312
+ },
3313
+ {
3314
+ "name": "metaId",
3315
+ "type": "text",
3316
+ "required": true,
3317
+ "columnType": "TEXT"
3318
+ },
3319
+ {
3320
+ "name": "role",
3321
+ "type": "text",
3322
+ "required": true,
3323
+ "columnType": "TEXT"
3324
+ },
3325
+ {
3326
+ "name": "sortOrder",
3327
+ "type": "integer",
3328
+ "required": false,
3329
+ "columnType": "INTEGER",
3330
+ "default": 0
3331
+ },
3332
+ {
3333
+ "name": "tenantId",
3334
+ "type": "text",
3335
+ "required": true,
3336
+ "columnType": "UUID"
3337
+ },
3338
+ {
3339
+ "name": "recordKind",
3340
+ "type": "text",
3341
+ "required": true,
3342
+ "columnType": "TEXT",
3343
+ "default": "question"
3344
+ },
3345
+ {
3346
+ "name": "action",
3347
+ "type": "text",
3348
+ "required": true,
3349
+ "columnType": "TEXT",
3350
+ "default": "created"
3351
+ },
3352
+ {
3353
+ "name": "actorType",
3354
+ "type": "text",
3355
+ "required": true,
3356
+ "columnType": "TEXT",
3357
+ "default": "system"
3358
+ },
3359
+ {
3360
+ "name": "actorId",
3361
+ "type": "text",
3362
+ "required": true,
3363
+ "columnType": "TEXT"
3364
+ },
3365
+ {
3366
+ "name": "fromStatus",
3367
+ "type": "text",
3368
+ "required": false,
3369
+ "columnType": "TEXT"
3370
+ },
3371
+ {
3372
+ "name": "toStatus",
3373
+ "type": "text",
3374
+ "required": true,
3375
+ "columnType": "TEXT"
3376
+ },
3377
+ {
3378
+ "name": "note",
3379
+ "type": "text",
3380
+ "required": false,
3381
+ "columnType": "TEXT"
3382
+ },
3383
+ {
3384
+ "name": "value",
3385
+ "type": "text",
3386
+ "required": false,
3387
+ "columnType": "TEXT"
3388
+ },
3389
+ {
3390
+ "name": "rationale",
3391
+ "type": "text",
3392
+ "required": false,
3393
+ "columnType": "TEXT"
3394
+ },
3395
+ {
3396
+ "name": "revision",
3397
+ "type": "integer",
3398
+ "required": true,
3399
+ "columnType": "INTEGER",
3400
+ "default": 1
3401
+ },
3402
+ {
3403
+ "name": "fromOwnerType",
3404
+ "type": "text",
3405
+ "required": false,
3406
+ "columnType": "TEXT",
3407
+ "default": ""
3408
+ },
3409
+ {
3410
+ "name": "fromOwnerId",
3411
+ "type": "text",
3412
+ "required": false,
3413
+ "columnType": "TEXT"
3414
+ },
3415
+ {
3416
+ "name": "toOwnerType",
3417
+ "type": "text",
3418
+ "required": false,
3419
+ "columnType": "TEXT",
3420
+ "default": ""
3421
+ },
3422
+ {
3423
+ "name": "toOwnerId",
3424
+ "type": "text",
3425
+ "required": false,
3426
+ "columnType": "TEXT"
3427
+ }
3428
+ ],
3429
+ "relationships": [],
3430
+ "methods": [
3431
+ "beforeDelete",
3432
+ "hydrate"
3433
+ ],
3434
+ "methodSignatures": [
3435
+ {
3436
+ "name": "beforeDelete",
3437
+ "async": true,
3438
+ "returns": "Promise<void>"
3439
+ },
3440
+ {
3441
+ "name": "hydrate",
3442
+ "async": true,
3443
+ "returns": "Promise<T | null>"
3444
+ }
3445
+ ],
3446
+ "tenant": {
3447
+ "scoped": true,
3448
+ "mode": "required",
3449
+ "field": "tenantId"
3450
+ },
3451
+ "conflictColumns": [
3452
+ "tenant_id",
3453
+ "slug",
3454
+ "context"
3455
+ ],
3456
+ "surfaces": [],
3457
+ "withheldSurfaces": [
3458
+ {
3459
+ "kind": "api",
3460
+ "operation": "beforeDelete",
3461
+ "code": "api-disabled",
3462
+ "reason": "api is disabled",
3463
+ "objectName": "@happyvertical/smrt-projects:ProjectGovernanceEvent"
3464
+ },
3465
+ {
3466
+ "kind": "api",
3467
+ "operation": "hydrate",
3468
+ "code": "api-disabled",
3469
+ "reason": "api is disabled",
3470
+ "objectName": "@happyvertical/smrt-projects:ProjectGovernanceEvent"
3471
+ }
3472
+ ],
3473
+ "relationshipFeatures": [
3474
+ "SmrtPolymorphicAssociation",
3475
+ "uuidColumns"
3476
+ ],
3477
+ "tags": [],
3478
+ "risks": []
3479
+ },
3480
+ {
3481
+ "name": "ProjectIntegration",
3482
+ "qualifiedName": "@happyvertical/smrt-projects:ProjectIntegration",
3483
+ "displayLabelField": "name",
3484
+ "collection": "projectintegrations",
3485
+ "tableName": "project_integrations",
3486
+ "packageName": "@happyvertical/smrt-projects",
3487
+ "extends": "SmrtObject",
3488
+ "fields": [
3489
+ {
3490
+ "name": "tenantId",
3491
+ "type": "text",
3492
+ "required": true,
3493
+ "columnType": "UUID"
3494
+ },
3495
+ {
3496
+ "name": "projectId",
3497
+ "type": "text",
3498
+ "required": false,
3499
+ "columnType": "TEXT",
3500
+ "default": ""
3501
+ },
3502
+ {
3503
+ "name": "name",
3504
+ "type": "text",
3505
+ "required": false,
3506
+ "columnType": "TEXT",
3507
+ "default": ""
3508
+ },
3509
+ {
3510
+ "name": "capabilities",
3511
+ "type": "json",
3512
+ "required": false,
3513
+ "columnType": "JSON",
3514
+ "default": []
3515
+ },
3516
+ {
3517
+ "name": "status",
3518
+ "type": "text",
3519
+ "required": false,
3520
+ "columnType": "TEXT",
3521
+ "default": "active"
3522
+ },
3523
+ {
3524
+ "name": "revokedAt",
3525
+ "type": "datetime",
3526
+ "required": false,
3527
+ "columnType": "TIMESTAMP"
3528
+ }
3529
+ ],
3530
+ "relationships": [],
3531
+ "methods": [
3532
+ "hasCapability",
3533
+ "isActive"
3534
+ ],
3535
+ "methodSignatures": [
3536
+ {
3537
+ "name": "hasCapability",
3538
+ "params": [
3539
+ "capability: ProjectIntegrationCapability"
3540
+ ],
3541
+ "returns": "boolean"
3542
+ },
3543
+ {
3544
+ "name": "isActive",
3545
+ "returns": "boolean"
3546
+ }
3547
+ ],
3548
+ "tenant": {
3549
+ "scoped": true,
3550
+ "mode": "required",
3551
+ "field": "tenantId"
3552
+ },
3553
+ "conflictColumns": [
3554
+ "tenant_id",
3555
+ "project_id",
3556
+ "name"
3557
+ ],
3558
+ "surfaces": [
3559
+ {
3560
+ "kind": "api",
3561
+ "name": "projectintegrations.list",
3562
+ "operation": "list",
3563
+ "objectName": "@happyvertical/smrt-projects:ProjectIntegration",
3564
+ "path": "/projectintegrations",
3565
+ "method": "GET"
3566
+ },
3567
+ {
3568
+ "kind": "api",
3569
+ "name": "projectintegrations.get",
3570
+ "operation": "get",
3571
+ "objectName": "@happyvertical/smrt-projects:ProjectIntegration",
3572
+ "path": "/projectintegrations/[id]",
3573
+ "method": "GET"
3574
+ },
3575
+ {
3576
+ "kind": "cli",
3577
+ "name": "projectintegration_list",
3578
+ "operation": "list",
3579
+ "objectName": "@happyvertical/smrt-projects:ProjectIntegration"
3006
3580
  },
3007
3581
  {
3008
3582
  "kind": "cli",
@@ -3108,6 +3682,298 @@
3108
3682
  "tags": [],
3109
3683
  "risks": []
3110
3684
  },
3685
+ {
3686
+ "name": "ProjectQuestion",
3687
+ "qualifiedName": "@happyvertical/smrt-projects:ProjectQuestion",
3688
+ "collection": "projectquestions",
3689
+ "tableName": "project_questions",
3690
+ "packageName": "@happyvertical/smrt-projects",
3691
+ "extends": "SmrtPolymorphicAssociation",
3692
+ "fields": [
3693
+ {
3694
+ "name": "metaType",
3695
+ "type": "text",
3696
+ "required": true,
3697
+ "columnType": "TEXT"
3698
+ },
3699
+ {
3700
+ "name": "metaId",
3701
+ "type": "text",
3702
+ "required": true,
3703
+ "columnType": "TEXT"
3704
+ },
3705
+ {
3706
+ "name": "role",
3707
+ "type": "text",
3708
+ "required": true,
3709
+ "columnType": "TEXT"
3710
+ },
3711
+ {
3712
+ "name": "sortOrder",
3713
+ "type": "integer",
3714
+ "required": false,
3715
+ "columnType": "INTEGER",
3716
+ "default": 0
3717
+ },
3718
+ {
3719
+ "name": "tenantId",
3720
+ "type": "text",
3721
+ "required": true,
3722
+ "columnType": "UUID"
3723
+ },
3724
+ {
3725
+ "name": "prompt",
3726
+ "type": "text",
3727
+ "required": true,
3728
+ "columnType": "TEXT"
3729
+ },
3730
+ {
3731
+ "name": "answer",
3732
+ "type": "text",
3733
+ "required": false,
3734
+ "columnType": "TEXT"
3735
+ },
3736
+ {
3737
+ "name": "status",
3738
+ "type": "text",
3739
+ "required": true,
3740
+ "columnType": "TEXT",
3741
+ "default": "open"
3742
+ },
3743
+ {
3744
+ "name": "ownerType",
3745
+ "type": "text",
3746
+ "required": false,
3747
+ "columnType": "TEXT",
3748
+ "default": ""
3749
+ },
3750
+ {
3751
+ "name": "ownerId",
3752
+ "type": "text",
3753
+ "required": false,
3754
+ "columnType": "TEXT"
3755
+ },
3756
+ {
3757
+ "name": "visibility",
3758
+ "type": "text",
3759
+ "required": false,
3760
+ "columnType": "TEXT",
3761
+ "default": "internal"
3762
+ },
3763
+ {
3764
+ "name": "contextReferences",
3765
+ "type": "text",
3766
+ "required": false,
3767
+ "columnType": "TEXT"
3768
+ },
3769
+ {
3770
+ "name": "revision",
3771
+ "type": "integer",
3772
+ "required": true,
3773
+ "columnType": "INTEGER",
3774
+ "default": 1
3775
+ },
3776
+ {
3777
+ "name": "answeredAt",
3778
+ "type": "datetime",
3779
+ "required": false,
3780
+ "columnType": "TIMESTAMP"
3781
+ }
3782
+ ],
3783
+ "relationships": [],
3784
+ "methods": [
3785
+ "getContextReferences",
3786
+ "hydrate",
3787
+ "setContextReferences"
3788
+ ],
3789
+ "methodSignatures": [
3790
+ {
3791
+ "name": "getContextReferences",
3792
+ "returns": "ProjectContextReference[]"
3793
+ },
3794
+ {
3795
+ "name": "hydrate",
3796
+ "async": true,
3797
+ "returns": "Promise<T | null>"
3798
+ },
3799
+ {
3800
+ "name": "setContextReferences",
3801
+ "params": [
3802
+ "value: ProjectContextReference[]"
3803
+ ],
3804
+ "returns": "void"
3805
+ }
3806
+ ],
3807
+ "tenant": {
3808
+ "scoped": true,
3809
+ "mode": "required",
3810
+ "field": "tenantId"
3811
+ },
3812
+ "conflictColumns": [
3813
+ "tenant_id",
3814
+ "slug",
3815
+ "context"
3816
+ ],
3817
+ "surfaces": [],
3818
+ "withheldSurfaces": [
3819
+ {
3820
+ "kind": "api",
3821
+ "operation": "getContextReferences",
3822
+ "code": "api-disabled",
3823
+ "reason": "api is disabled",
3824
+ "objectName": "@happyvertical/smrt-projects:ProjectQuestion"
3825
+ },
3826
+ {
3827
+ "kind": "api",
3828
+ "operation": "hydrate",
3829
+ "code": "api-disabled",
3830
+ "reason": "api is disabled",
3831
+ "objectName": "@happyvertical/smrt-projects:ProjectQuestion"
3832
+ },
3833
+ {
3834
+ "kind": "api",
3835
+ "operation": "setContextReferences",
3836
+ "code": "api-disabled",
3837
+ "reason": "api is disabled",
3838
+ "objectName": "@happyvertical/smrt-projects:ProjectQuestion"
3839
+ }
3840
+ ],
3841
+ "relationshipFeatures": [
3842
+ "SmrtPolymorphicAssociation",
3843
+ "uuidColumns"
3844
+ ],
3845
+ "tags": [],
3846
+ "risks": []
3847
+ },
3848
+ {
3849
+ "name": "ProjectSpecification",
3850
+ "qualifiedName": "@happyvertical/smrt-projects:ProjectSpecification",
3851
+ "displayLabelField": "title",
3852
+ "collection": "projectspecifications",
3853
+ "description": "Canonical project requirements and their portable application cookbook.",
3854
+ "tableName": "project_specifications",
3855
+ "packageName": "@happyvertical/smrt-projects",
3856
+ "extends": "SmrtPolymorphicAssociation",
3857
+ "fields": [
3858
+ {
3859
+ "name": "metaType",
3860
+ "type": "text",
3861
+ "required": true,
3862
+ "columnType": "TEXT"
3863
+ },
3864
+ {
3865
+ "name": "metaId",
3866
+ "type": "text",
3867
+ "required": true,
3868
+ "columnType": "TEXT"
3869
+ },
3870
+ {
3871
+ "name": "role",
3872
+ "type": "text",
3873
+ "required": true,
3874
+ "columnType": "TEXT"
3875
+ },
3876
+ {
3877
+ "name": "sortOrder",
3878
+ "type": "integer",
3879
+ "required": false,
3880
+ "columnType": "INTEGER",
3881
+ "default": 0
3882
+ },
3883
+ {
3884
+ "name": "tenantId",
3885
+ "type": "text",
3886
+ "required": true,
3887
+ "columnType": "UUID"
3888
+ },
3889
+ {
3890
+ "name": "title",
3891
+ "type": "text",
3892
+ "required": true,
3893
+ "columnType": "TEXT"
3894
+ },
3895
+ {
3896
+ "name": "summary",
3897
+ "type": "text",
3898
+ "required": false,
3899
+ "columnType": "TEXT"
3900
+ },
3901
+ {
3902
+ "name": "cookbook",
3903
+ "type": "text",
3904
+ "required": false,
3905
+ "columnType": "TEXT"
3906
+ },
3907
+ {
3908
+ "name": "revision",
3909
+ "type": "integer",
3910
+ "required": true,
3911
+ "columnType": "INTEGER",
3912
+ "default": 1
3913
+ },
3914
+ {
3915
+ "name": "actorType",
3916
+ "type": "text",
3917
+ "required": false,
3918
+ "columnType": "TEXT"
3919
+ },
3920
+ {
3921
+ "name": "actorId",
3922
+ "type": "text",
3923
+ "required": false,
3924
+ "columnType": "TEXT"
3925
+ }
3926
+ ],
3927
+ "relationships": [],
3928
+ "methods": [
3929
+ "getCookbook",
3930
+ "hydrate"
3931
+ ],
3932
+ "methodSignatures": [
3933
+ {
3934
+ "name": "getCookbook",
3935
+ "returns": "Cookbook"
3936
+ },
3937
+ {
3938
+ "name": "hydrate",
3939
+ "async": true,
3940
+ "returns": "Promise<T | null>"
3941
+ }
3942
+ ],
3943
+ "tenant": {
3944
+ "scoped": true,
3945
+ "mode": "required",
3946
+ "field": "tenantId"
3947
+ },
3948
+ "conflictColumns": [
3949
+ "tenant_id",
3950
+ "meta_type",
3951
+ "meta_id"
3952
+ ],
3953
+ "surfaces": [],
3954
+ "withheldSurfaces": [
3955
+ {
3956
+ "kind": "api",
3957
+ "operation": "getCookbook",
3958
+ "code": "api-disabled",
3959
+ "reason": "api is disabled",
3960
+ "objectName": "@happyvertical/smrt-projects:ProjectSpecification"
3961
+ },
3962
+ {
3963
+ "kind": "api",
3964
+ "operation": "hydrate",
3965
+ "code": "api-disabled",
3966
+ "reason": "api is disabled",
3967
+ "objectName": "@happyvertical/smrt-projects:ProjectSpecification"
3968
+ }
3969
+ ],
3970
+ "relationshipFeatures": [
3971
+ "SmrtPolymorphicAssociation",
3972
+ "uuidColumns"
3973
+ ],
3974
+ "tags": [],
3975
+ "risks": []
3976
+ },
3111
3977
  {
3112
3978
  "name": "PullRequest",
3113
3979
  "qualifiedName": "@happyvertical/smrt-projects:PullRequest",
@@ -5267,10 +6133,10 @@
5267
6133
  "crossPackageRefFields": 0,
5268
6134
  "junctionCollections": 0,
5269
6135
  "hierarchicalObjects": 0,
5270
- "polymorphicAssociations": 0,
5271
- "uuidColumns": 87
6136
+ "polymorphicAssociations": 4,
6137
+ "uuidColumns": 103
5272
6138
  },
5273
- "agentDoc": "# @happyvertical/smrt-projects\n\nProvider-agnostic project management plus the managed-application delivery\ncontrol plane. Repository and board providers remain canonical; managed apps\nonly receive scoped Project Integration credentials and provider-neutral\nrequest/delivery projections.\n\n## Managed application delivery (#1949)\n\n- `ProjectIntegration` stores capability grants and a sensitive credential\n hash; the raw credential is returned once by\n `ProjectIntegrationCollection.provision()`/`rotate()` and is never persisted.\n Provisioning, rotation, and revocation are append-only audited.\n- The stable requester identity is supplied when authenticating\n `ManagedProjectClient`; request reads are always restricted to that requester\n and integration.\n- `DevelopmentRequest` preserves evidence, origin, discussion, visibility, and\n lifecycle history. `ManagedProjectClient` owns managed intake and\n `DevelopmentRequestService` adds internal triage and work projection.\n- `DevelopmentRequestWorkLink` connects zero-to-many provider-neutral work\n items. Canonical status stays on the link and drives request lifecycle\n projection without granting the managed app provider credentials.\n- `ProjectDeliveryEvent` is idempotent per integration key and sequenced for\n replay. Preview decisions flow through the control-plane adapter, never\n directly to repository providers.\n- `AssistanceRequest` preserves conversational intake before lossless routing\n to Support, Development, or both. `smrt-support` supplies the concrete\n `ProjectAssistanceSupportAdapter`.\n- `ManagedProjectClient` deliberately exposes no repository or board client;\n delivery and assistance use separate capability-gated service facades.\n\n## Shared Professional Service evidence (#1955, #3288)\n\n`ServiceTimeEntry`, `ServiceChargeSnapshot`, `ServiceCompensationSnapshot`,\ntheir collections, and `ServiceEvidenceService` are owned by\n`@happyvertical/smrt-timesheets` (#3288) and **re-exported** here unchanged,\non the unchanged `service_time_entries` / snapshot tables. Do not reintroduce\na projects subclass: a third same-named class on the table breaks the\nsingle-table-family resolution (see `packages/timesheets/AGENTS.md`).\n`caseId` / `specialistId` exist only on smrt-support's subtype;\n`SubscriptionServiceCommercialResolver` reads them duck-typed when a support\nentry is priced. It prices client work through `smrt-subscriptions` (#1925);\nprovider compensation remains a separate resolver and snapshot. The\ntime-entry Svelte components are re-exported from smrt-timesheets too. See\n`SERVICE_TIME_MIGRATION.md`.\n\n## Models\n\n| Model | Key Fields | Notes |\n|-------|-----------|-------|\n| **Repository** | `owner`, `name`, `providerType`, `tokenConfigKey` | `sync()`, `getIssues()`, `getPullRequests()` |\n| **Issue** | `repositoryId` (optional FK), `projectId` (optional FK), `number`, `title`, `body`, `state`, `labels[]` | `incorporateFeedback()`, `rollback()`, `suggestLabels()` |\n| **PullRequest** | extends Issue + `headRef`, `baseRef`, `merged`, `draft` | STI on Issue table. `summarize()`, `merge()` |\n| **Project** | `projectId`, `title`, `statuses[]`, `statusFieldId` | GitHub Projects V2. `addItem()`, `moveItem()`, `analyzeHealth()` |\n| **Comment** | `issueId` (FK), `body`, `authorLogin` | AI analysis support |\n| **Label** | `repositoryId` (optional FK), `projectId` (optional FK), `name`, `color` | Scope: repository, project, or neither |\n\n## Key Patterns\n\n- **Repository is optional (project-native issues)**: an Issue/Label may carry only a `projectId`. `issue.hasRepository()` gates every provider call: `sync()` is a no-op, `close/addLabels/removeLabel/assign/addComment/rollback/incorporateFeedback(apply)` change local state only, `getComments()` reads stored `Comment` rows, `getRepository()` throws, `getUrl()` returns `''`. No unique key spans `repositoryId`+`number` (lookups only), so NULL repositories need no partial index. Schema change adds `issues.project_id`/`labels.project_id` and relaxes `repository_id` to nullable on both; `db:migrate` generates it from the manifest.\n\n- **Token config reference**: stores env var name (`tokenConfigKey: 'GITHUB_TOKEN'`), not the token itself. Resolved at runtime from `process.env` or `getModuleConfig()`\n- **Living spec** (`incorporateFeedback()`): AI synthesizes issue comments into updated body. Supports preview mode and `rollback()`\n- **Provider-agnostic**: GitHub primary, GitLab/Bitbucket/Azure planned. Uses `@happyvertical/repos` and `@happyvertical/projects` SDK packages\n- **PullRequest is STI on Issue**: shares table, discriminated by `_meta_type`\n\n## Collection Methods\n\nAll collections provide: `discover({ repository, filters })`, `findByRepository(repoId)`, `findOpen(repoId?)`, `batchSync(repository)`.\n\n## Gotchas\n\n- **Money is integer minor units** (cents) — `$19.99` is `1999`.\n `ServiceChargeSnapshot.amount` and `ServiceCompensationSnapshot.amount`\n (declared in smrt-timesheets)\n initialize `= 0`, never `= 0.0`: the integer literal is what maps them to\n INTEGER columns (BIGINT on fresh PostgreSQL/DuckDB databases; #2401, #2373). The two convert as a pair — the delivery margin is\n `charge - compensation`, an exact integer subtraction with no tolerance —\n and both are fed verbatim by `CommercialSnapshot.amount`, so a resolver that\n returns major units is wrong on every engine: snapshot `save()` refuses a\n fractional value (`19.99`) on SQLite and PostgreSQL alike, but a whole\n major-unit value (`150` for $150.00) is a valid integer and is stored\n 100× too small. `pnpm --filter @happyvertical/smrt-timesheets\n test:postgres` is the lane that holds the line (moved with the snapshots in\n #3288).\n- **UI formats by dividing, never by `toFixed`**: `formatCurrency()` and\n `ServiceEvidenceList`'s `money()` scale back to major units using the\n currency's own minor-unit exponent, so zero-decimal currencies (JPY, KRW) are\n not divided.\n- **Migrating an existing database**: `preflightProjectsMoneyMinorUnits(db)`\n reports which columns still hold major units and which rows would be rounded\n or exceed JavaScript's safe-integer range; `migrateProjectsMoneyToMinorUnits(db)` converts them\n (idempotent via `_smrt_backfills`). On SQLite the values are rescaled but the\n declared type needs the table-rebuild path (#2370). Fresh PostgreSQL/DuckDB\n INTEGER columns are BIGINT and hydration rejects values outside JavaScript's\n safe-integer range; existing PostgreSQL `int4` columns require #2424.\n- **SDK dependency**: requires `@happyvertical/repos` and `@happyvertical/projects` from SDK\n- **tokenConfigKey not tokenValue**: never store actual tokens in the database\n- **synthesisCount tracks incorporateFeedback calls**: incremented on each apply\n",
6139
+ "agentDoc": "# @happyvertical/smrt-projects\n\nProvider-agnostic project management plus the managed-application delivery\ncontrol plane. Repository and board providers remain canonical; managed apps\nonly receive scoped Project Integration credentials and provider-neutral\nrequest/delivery projections.\n\n## Managed application delivery (#1949)\n\n- `ProjectIntegration` stores capability grants and a sensitive credential\n hash; the raw credential is returned once by\n `ProjectIntegrationCollection.provision()`/`rotate()` and is never persisted.\n Provisioning, rotation, and revocation are append-only audited.\n- The stable requester identity is supplied when authenticating\n `ManagedProjectClient`; request reads are always restricted to that requester\n and integration.\n- `DevelopmentRequest` preserves evidence, origin, discussion, visibility, and\n lifecycle history. `ManagedProjectClient` owns managed intake and\n `DevelopmentRequestService` adds internal triage and work projection.\n- `DevelopmentRequestWorkLink` connects zero-to-many provider-neutral work\n items. Canonical status stays on the link and drives request lifecycle\n projection without granting the managed app provider credentials.\n- `ProjectDeliveryEvent` is idempotent per integration key and sequenced for\n replay. Preview decisions flow through the control-plane adapter, never\n directly to repository providers.\n- `AssistanceRequest` preserves conversational intake before lossless routing\n to Support, Development, or both. `smrt-support` supplies the concrete\n `ProjectAssistanceSupportAdapter`.\n- `ManagedProjectClient` deliberately exposes no repository or board client;\n delivery and assistance use separate capability-gated service facades.\n\n## Shared Professional Service evidence (#1955, #3288)\n\n`ServiceTimeEntry`, `ServiceChargeSnapshot`, `ServiceCompensationSnapshot`,\ntheir collections, and `ServiceEvidenceService` are owned by\n`@happyvertical/smrt-timesheets` (#3288) and **re-exported** here unchanged,\non the unchanged `service_time_entries` / snapshot tables. Do not reintroduce\na projects subclass: a third same-named class on the table breaks the\nsingle-table-family resolution (see `packages/timesheets/AGENTS.md`).\n`caseId` / `specialistId` exist only on smrt-support's subtype;\n`SubscriptionServiceCommercialResolver` reads them duck-typed when a support\nentry is priced. It prices client work through `smrt-subscriptions` (#1925);\nprovider compensation remains a separate resolver and snapshot. The\ntime-entry Svelte components are re-exported from smrt-timesheets too. See\n`SERVICE_TIME_MIGRATION.md`.\n\n## Models\n\n| Model | Key Fields | Notes |\n|-------|-----------|-------|\n| **Repository** | `owner`, `name`, `providerType`, `tokenConfigKey` | `sync()`, `getIssues()`, `getPullRequests()` |\n| **Issue** | `repositoryId` (optional FK), `projectId` (optional FK), `number`, `title`, `body`, `state`, `labels[]` | `incorporateFeedback()`, `rollback()`, `suggestLabels()` |\n| **PullRequest** | extends Issue + `headRef`, `baseRef`, `merged`, `draft` | STI on Issue table. `summarize()`, `merge()` |\n| **Project** | `projectId`, `title`, `statuses[]`, `statusFieldId` | GitHub Projects V2. `addItem()`, `moveItem()`, `analyzeHealth()` |\n| **ProjectQuestion** | polymorphic project target, `prompt`, `answer`, owner, `status`, `revision` | Owner question lifecycle; mutate through `ProjectGovernanceService`. |\n| **ProjectDecision** | polymorphic project target, options, `outcome`, `rationale`, owner, `status`, `revision` | Project choice lifecycle; mutate through `ProjectGovernanceService`. |\n| **ProjectGovernanceEvent** | question/decision target, actor, action, status, revision | Immutable lifecycle history. |\n| **Comment** | `issueId` (FK), `body`, `authorLogin` | AI analysis support |\n| **Label** | `repositoryId` (optional FK), `projectId` (optional FK), `name`, `color` | Scope: repository, project, or neither |\n\n## Key Patterns\n\n- **Repository is optional (project-native issues)**: an Issue/Label may carry only a `projectId`. `issue.hasRepository()` gates every provider call: `sync()` is a no-op, `close/addLabels/removeLabel/assign/addComment/rollback/incorporateFeedback(apply)` change local state only, `getComments()` reads stored `Comment` rows, `getRepository()` throws, `getUrl()` returns `''`. No unique key spans `repositoryId`+`number` (lookups only), so NULL repositories need no partial index. Schema change adds `issues.project_id`/`labels.project_id` and relaxes `repository_id` to nullable on both; `db:migrate` generates it from the manifest.\n\n- **Token config reference**: stores env var name (`tokenConfigKey: 'GITHUB_TOKEN'`), not the token itself. Resolved at runtime from `process.env` or `getModuleConfig()`\n- **Living spec** (`incorporateFeedback()`): AI synthesizes issue comments into updated body. Supports preview mode and `rollback()`\n- **Provider-agnostic**: GitHub primary, GitLab/Bitbucket/Azure planned. Uses `@happyvertical/repos` and `@happyvertical/projects` SDK packages\n- **PullRequest is STI on Issue**: shares table, discriminated by `_meta_type`\n\n## Collection Methods\n\nAll collections provide: `discover({ repository, filters })`, `findByRepository(repoId)`, `findOpen(repoId?)`, `batchSync(repository)`.\n\n## Gotchas\n\n- **Money is integer minor units** (cents) — `$19.99` is `1999`.\n `ServiceChargeSnapshot.amount` and `ServiceCompensationSnapshot.amount`\n (declared in smrt-timesheets)\n initialize `= 0`, never `= 0.0`: the integer literal is what maps them to\n INTEGER columns (BIGINT on fresh PostgreSQL/DuckDB databases; #2401, #2373). The two convert as a pair — the delivery margin is\n `charge - compensation`, an exact integer subtraction with no tolerance —\n and both are fed verbatim by `CommercialSnapshot.amount`, so a resolver that\n returns major units is wrong on every engine: snapshot `save()` refuses a\n fractional value (`19.99`) on SQLite and PostgreSQL alike, but a whole\n major-unit value (`150` for $150.00) is a valid integer and is stored\n 100× too small. `pnpm --filter @happyvertical/smrt-timesheets\n test:postgres` is the lane that holds the line (moved with the snapshots in\n #3288).\n- **UI formats by dividing, never by `toFixed`**: `formatCurrency()` and\n `ServiceEvidenceList`'s `money()` scale back to major units using the\n currency's own minor-unit exponent, so zero-decimal currencies (JPY, KRW) are\n not divided.\n- **Migrating an existing database**: `preflightProjectsMoneyMinorUnits(db)`\n reports which columns still hold major units and which rows would be rounded\n or exceed JavaScript's safe-integer range; `migrateProjectsMoneyToMinorUnits(db)` converts them\n (idempotent via `_smrt_backfills`). On SQLite the values are rescaled but the\n declared type needs the table-rebuild path (#2370). Fresh PostgreSQL/DuckDB\n INTEGER columns are BIGINT and hydration rejects values outside JavaScript's\n safe-integer range; existing PostgreSQL `int4` columns require #2424.\n- **SDK dependency**: requires `@happyvertical/repos` and `@happyvertical/projects` from SDK\n- **tokenConfigKey not tokenValue**: never store actual tokens in the database\n- **synthesisCount tracks incorporateFeedback calls**: incremented on each apply\n",
5274
6140
  "recipes": [
5275
6141
  {
5276
6142
  "id": "projects.tracker",