@happyvertical/smrt-projects 0.55.6 → 0.55.8

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.
@@ -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.55.6",
6
+ "packageVersion": "0.55.8",
7
7
  "sourceManifestPath": "dist/manifest.json",
8
8
  "agentDocPath": "AGENTS.md",
9
9
  "sourceHashes": {
10
- "manifest": "73fd367068244b6ee05f326fa7c52c6345f729b6a706b038b2ac1db4806d5d8f",
11
- "packageJson": "2246018a856fc65f5e8fc573c1b46836b4632bbb4fef674e68e0798327842982",
12
- "agents": "0bfcad468f2684901c34ae04db8a82343060ed97f9917ca415a82501fefed78d"
10
+ "manifest": "9b4beb24d74e64b7e5b50382d0ab7ab50cc1b3015d5a487b790743d2beac9bfc",
11
+ "packageJson": "1cafa7e6e0721830898133f6d527b25a981b08eb0b7791b80bf4e61efaed0aa1",
12
+ "agents": "014098eac2bb1699b653ccd3029e062a2312a521862e39225dd8ca73b6ae8206"
13
13
  },
14
14
  "exports": [
15
15
  ".",
@@ -83,6 +83,23 @@
83
83
  "sti-inheritance"
84
84
  ],
85
85
  "objects": [
86
+ {
87
+ "name": "CommentCollection",
88
+ "qualifiedName": "@happyvertical/smrt-projects:CommentCollection",
89
+ "collection": "comments",
90
+ "tableName": "comments",
91
+ "packageName": "@happyvertical/smrt-projects",
92
+ "extends": "SmrtCollection",
93
+ "fields": [],
94
+ "relationships": [],
95
+ "methods": [],
96
+ "surfaces": [],
97
+ "relationshipFeatures": [
98
+ "uuidColumns"
99
+ ],
100
+ "tags": [],
101
+ "risks": []
102
+ },
86
103
  {
87
104
  "name": "DevelopmentRequestHistoryCollection",
88
105
  "qualifiedName": "@happyvertical/smrt-projects:DevelopmentRequestHistoryCollection",
@@ -227,6 +244,7 @@
227
244
  "findByAssignee",
228
245
  "findByLabel",
229
246
  "findByNumber",
247
+ "findByProject",
230
248
  "findByRepository",
231
249
  "findByTenant",
232
250
  "findGlobal",
@@ -280,6 +298,14 @@
280
298
  ],
281
299
  "returns": "Promise<Issue | null>"
282
300
  },
301
+ {
302
+ "name": "findByProject",
303
+ "async": true,
304
+ "params": [
305
+ "projectId: string"
306
+ ],
307
+ "returns": "Promise<Issue[]>"
308
+ },
283
309
  {
284
310
  "name": "findByRepository",
285
311
  "async": true,
@@ -335,6 +361,14 @@
335
361
  }
336
362
  ],
337
363
  "surfaces": [
364
+ {
365
+ "kind": "api",
366
+ "name": "issues.findByProject",
367
+ "operation": "findByProject",
368
+ "objectName": "@happyvertical/smrt-projects:IssueCollection",
369
+ "path": "/issues/findByProject",
370
+ "method": "POST"
371
+ },
338
372
  {
339
373
  "kind": "api",
340
374
  "name": "issues.findByRepository",
@@ -421,6 +455,12 @@
421
455
  "operation": "discover",
422
456
  "objectName": "@happyvertical/smrt-projects:IssueCollection"
423
457
  },
458
+ {
459
+ "kind": "cli",
460
+ "name": "issuecollection_findByProject",
461
+ "operation": "findByProject",
462
+ "objectName": "@happyvertical/smrt-projects:IssueCollection"
463
+ },
424
464
  {
425
465
  "kind": "cli",
426
466
  "name": "issuecollection_findByRepository",
@@ -493,6 +533,12 @@
493
533
  "operation": "discover",
494
534
  "objectName": "@happyvertical/smrt-projects:IssueCollection"
495
535
  },
536
+ {
537
+ "kind": "mcp",
538
+ "name": "issuecollection_findbyproject",
539
+ "operation": "findByProject",
540
+ "objectName": "@happyvertical/smrt-projects:IssueCollection"
541
+ },
496
542
  {
497
543
  "kind": "mcp",
498
544
  "name": "issuecollection_findbyrepository",
@@ -1826,7 +1872,8 @@
1826
1872
  "type": "foreignKey",
1827
1873
  "required": false,
1828
1874
  "related": "Issue",
1829
- "columnType": "UUID"
1875
+ "columnType": "UUID",
1876
+ "description": "The issue this comment is on."
1830
1877
  },
1831
1878
  {
1832
1879
  "name": "commentId",
@@ -1840,14 +1887,16 @@
1840
1887
  "type": "text",
1841
1888
  "required": false,
1842
1889
  "columnType": "TEXT",
1843
- "default": ""
1890
+ "default": "",
1891
+ "description": "The comment text."
1844
1892
  },
1845
1893
  {
1846
1894
  "name": "author",
1847
1895
  "type": "text",
1848
1896
  "required": false,
1849
1897
  "columnType": "TEXT",
1850
- "default": ""
1898
+ "default": "",
1899
+ "description": "Who wrote it."
1851
1900
  },
1852
1901
  {
1853
1902
  "name": "createdAt",
@@ -2300,10 +2349,18 @@
2300
2349
  {
2301
2350
  "name": "repositoryId",
2302
2351
  "type": "foreignKey",
2303
- "required": true,
2352
+ "required": false,
2304
2353
  "related": "Repository",
2305
2354
  "columnType": "UUID"
2306
2355
  },
2356
+ {
2357
+ "name": "projectId",
2358
+ "type": "foreignKey",
2359
+ "required": false,
2360
+ "related": "Project",
2361
+ "columnType": "UUID",
2362
+ "description": "The project this issue belongs to."
2363
+ },
2307
2364
  {
2308
2365
  "name": "number",
2309
2366
  "type": "integer",
@@ -2323,42 +2380,48 @@
2323
2380
  "type": "text",
2324
2381
  "required": false,
2325
2382
  "columnType": "TEXT",
2326
- "default": ""
2383
+ "default": "",
2384
+ "description": "A short summary of the issue."
2327
2385
  },
2328
2386
  {
2329
2387
  "name": "body",
2330
2388
  "type": "text",
2331
2389
  "required": false,
2332
2390
  "columnType": "TEXT",
2333
- "default": ""
2391
+ "default": "",
2392
+ "description": "The full description."
2334
2393
  },
2335
2394
  {
2336
2395
  "name": "state",
2337
2396
  "type": "text",
2338
2397
  "required": false,
2339
2398
  "columnType": "TEXT",
2340
- "default": "open"
2399
+ "default": "open",
2400
+ "description": "open or closed."
2341
2401
  },
2342
2402
  {
2343
2403
  "name": "author",
2344
2404
  "type": "text",
2345
2405
  "required": false,
2346
2406
  "columnType": "TEXT",
2347
- "default": ""
2407
+ "default": "",
2408
+ "description": "Who raised the issue."
2348
2409
  },
2349
2410
  {
2350
2411
  "name": "labels",
2351
2412
  "type": "json",
2352
2413
  "required": false,
2353
2414
  "columnType": "JSON",
2354
- "default": []
2415
+ "default": [],
2416
+ "description": "The labels attached to the issue."
2355
2417
  },
2356
2418
  {
2357
2419
  "name": "assignees",
2358
2420
  "type": "json",
2359
2421
  "required": false,
2360
2422
  "columnType": "JSON",
2361
- "default": []
2423
+ "default": [],
2424
+ "description": "The people working on it."
2362
2425
  },
2363
2426
  {
2364
2427
  "name": "commentsCount",
@@ -2392,9 +2455,16 @@
2392
2455
  {
2393
2456
  "name": "repositoryId",
2394
2457
  "type": "foreignKey",
2395
- "required": true,
2458
+ "required": false,
2396
2459
  "related": "Repository",
2397
2460
  "columnType": "UUID"
2461
+ },
2462
+ {
2463
+ "name": "projectId",
2464
+ "type": "foreignKey",
2465
+ "required": false,
2466
+ "related": "Project",
2467
+ "columnType": "UUID"
2398
2468
  }
2399
2469
  ],
2400
2470
  "methods": [
@@ -2407,6 +2477,7 @@
2407
2477
  "getComments",
2408
2478
  "getRepository",
2409
2479
  "getUrl",
2480
+ "hasRepository",
2410
2481
  "incorporateFeedback",
2411
2482
  "isBugReport",
2412
2483
  "isFeatureRequest",
@@ -2469,6 +2540,10 @@
2469
2540
  "name": "getUrl",
2470
2541
  "returns": "string"
2471
2542
  },
2543
+ {
2544
+ "name": "hasRepository",
2545
+ "returns": "boolean"
2546
+ },
2472
2547
  {
2473
2548
  "name": "incorporateFeedback",
2474
2549
  "async": true,
@@ -2686,6 +2761,13 @@
2686
2761
  "reason": "not listed in api.include",
2687
2762
  "objectName": "@happyvertical/smrt-projects:Issue"
2688
2763
  },
2764
+ {
2765
+ "kind": "api",
2766
+ "operation": "hasRepository",
2767
+ "code": "not-included",
2768
+ "reason": "not listed in api.include",
2769
+ "objectName": "@happyvertical/smrt-projects:Issue"
2770
+ },
2689
2771
  {
2690
2772
  "kind": "api",
2691
2773
  "operation": "incorporateFeedback",
@@ -2766,26 +2848,37 @@
2766
2848
  "related": "Repository",
2767
2849
  "columnType": "UUID"
2768
2850
  },
2851
+ {
2852
+ "name": "projectId",
2853
+ "type": "foreignKey",
2854
+ "required": false,
2855
+ "related": "Project",
2856
+ "columnType": "UUID",
2857
+ "description": "The project this label belongs to."
2858
+ },
2769
2859
  {
2770
2860
  "name": "name",
2771
2861
  "type": "text",
2772
2862
  "required": false,
2773
2863
  "columnType": "TEXT",
2774
- "default": ""
2864
+ "default": "",
2865
+ "description": "The label's name, for example bug."
2775
2866
  },
2776
2867
  {
2777
2868
  "name": "color",
2778
2869
  "type": "text",
2779
2870
  "required": false,
2780
2871
  "columnType": "TEXT",
2781
- "default": ""
2872
+ "default": "",
2873
+ "description": "The label's colour."
2782
2874
  },
2783
2875
  {
2784
2876
  "name": "description",
2785
2877
  "type": "text",
2786
2878
  "required": false,
2787
2879
  "columnType": "TEXT",
2788
- "default": ""
2880
+ "default": "",
2881
+ "description": "What the label means."
2789
2882
  }
2790
2883
  ],
2791
2884
  "relationships": [
@@ -2795,6 +2888,13 @@
2795
2888
  "required": false,
2796
2889
  "related": "Repository",
2797
2890
  "columnType": "UUID"
2891
+ },
2892
+ {
2893
+ "name": "projectId",
2894
+ "type": "foreignKey",
2895
+ "required": false,
2896
+ "related": "Project",
2897
+ "columnType": "UUID"
2798
2898
  }
2799
2899
  ],
2800
2900
  "methods": [
@@ -3032,28 +3132,32 @@
3032
3132
  "type": "text",
3033
3133
  "required": false,
3034
3134
  "columnType": "TEXT",
3035
- "default": ""
3135
+ "default": "",
3136
+ "description": "The project's name."
3036
3137
  },
3037
3138
  {
3038
3139
  "name": "description",
3039
3140
  "type": "text",
3040
3141
  "required": false,
3041
3142
  "columnType": "TEXT",
3042
- "default": ""
3143
+ "default": "",
3144
+ "description": "What the project is about."
3043
3145
  },
3044
3146
  {
3045
3147
  "name": "owner",
3046
3148
  "type": "text",
3047
3149
  "required": false,
3048
3150
  "columnType": "TEXT",
3049
- "default": ""
3151
+ "default": "",
3152
+ "description": "Who owns or runs the project."
3050
3153
  },
3051
3154
  {
3052
3155
  "name": "url",
3053
3156
  "type": "text",
3054
3157
  "required": false,
3055
3158
  "columnType": "TEXT",
3056
- "default": ""
3159
+ "default": "",
3160
+ "description": "A link to the project elsewhere."
3057
3161
  },
3058
3162
  {
3059
3163
  "name": "providerType",
@@ -3692,10 +3796,18 @@
3692
3796
  {
3693
3797
  "name": "repositoryId",
3694
3798
  "type": "foreignKey",
3695
- "required": true,
3799
+ "required": false,
3696
3800
  "related": "Repository",
3697
3801
  "columnType": "UUID"
3698
3802
  },
3803
+ {
3804
+ "name": "projectId",
3805
+ "type": "foreignKey",
3806
+ "required": false,
3807
+ "related": "Project",
3808
+ "columnType": "UUID",
3809
+ "description": "The project this issue belongs to."
3810
+ },
3699
3811
  {
3700
3812
  "name": "number",
3701
3813
  "type": "integer",
@@ -3715,42 +3827,48 @@
3715
3827
  "type": "text",
3716
3828
  "required": false,
3717
3829
  "columnType": "TEXT",
3718
- "default": ""
3830
+ "default": "",
3831
+ "description": "A short summary of the issue."
3719
3832
  },
3720
3833
  {
3721
3834
  "name": "body",
3722
3835
  "type": "text",
3723
3836
  "required": false,
3724
3837
  "columnType": "TEXT",
3725
- "default": ""
3838
+ "default": "",
3839
+ "description": "The full description."
3726
3840
  },
3727
3841
  {
3728
3842
  "name": "state",
3729
3843
  "type": "text",
3730
3844
  "required": false,
3731
3845
  "columnType": "TEXT",
3732
- "default": "open"
3846
+ "default": "open",
3847
+ "description": "open or closed."
3733
3848
  },
3734
3849
  {
3735
3850
  "name": "author",
3736
3851
  "type": "text",
3737
3852
  "required": false,
3738
3853
  "columnType": "TEXT",
3739
- "default": ""
3854
+ "default": "",
3855
+ "description": "Who raised the issue."
3740
3856
  },
3741
3857
  {
3742
3858
  "name": "labels",
3743
3859
  "type": "json",
3744
3860
  "required": false,
3745
3861
  "columnType": "JSON",
3746
- "default": []
3862
+ "default": [],
3863
+ "description": "The labels attached to the issue."
3747
3864
  },
3748
3865
  {
3749
3866
  "name": "assignees",
3750
3867
  "type": "json",
3751
3868
  "required": false,
3752
3869
  "columnType": "JSON",
3753
- "default": []
3870
+ "default": [],
3871
+ "description": "The people working on it."
3754
3872
  },
3755
3873
  {
3756
3874
  "name": "commentsCount",
@@ -3846,9 +3964,16 @@
3846
3964
  {
3847
3965
  "name": "repositoryId",
3848
3966
  "type": "foreignKey",
3849
- "required": true,
3967
+ "required": false,
3850
3968
  "related": "Repository",
3851
3969
  "columnType": "UUID"
3970
+ },
3971
+ {
3972
+ "name": "projectId",
3973
+ "type": "foreignKey",
3974
+ "required": false,
3975
+ "related": "Project",
3976
+ "columnType": "UUID"
3852
3977
  }
3853
3978
  ],
3854
3979
  "methods": [
@@ -3864,6 +3989,7 @@
3864
3989
  "getComments",
3865
3990
  "getRepository",
3866
3991
  "getUrl",
3992
+ "hasRepository",
3867
3993
  "incorporateFeedback",
3868
3994
  "isBugReport",
3869
3995
  "isFeatureRequest",
@@ -3946,6 +4072,10 @@
3946
4072
  "name": "getUrl",
3947
4073
  "returns": "string"
3948
4074
  },
4075
+ {
4076
+ "name": "hasRepository",
4077
+ "returns": "boolean"
4078
+ },
3949
4079
  {
3950
4080
  "name": "incorporateFeedback",
3951
4081
  "async": true,
@@ -4226,6 +4356,13 @@
4226
4356
  "reason": "not listed in api.include",
4227
4357
  "objectName": "@happyvertical/smrt-projects:PullRequest"
4228
4358
  },
4359
+ {
4360
+ "kind": "api",
4361
+ "operation": "hasRepository",
4362
+ "code": "not-included",
4363
+ "reason": "not listed in api.include",
4364
+ "objectName": "@happyvertical/smrt-projects:PullRequest"
4365
+ },
4229
4366
  {
4230
4367
  "kind": "api",
4231
4368
  "operation": "incorporateFeedback",
@@ -4673,6 +4810,7 @@
4673
4810
  "name": "DevelopmentRequestWorkLink",
4674
4811
  "qualifiedName": "@happyvertical/smrt-projects:DevelopmentRequestWorkLink",
4675
4812
  "collection": "developmentrequestworklinks",
4813
+ "description": "Provider-neutral connection from a Development Request to canonical work.",
4676
4814
  "tableName": "development_request_work_links",
4677
4815
  "packageName": "@happyvertical/smrt-projects",
4678
4816
  "extends": "SmrtObject",
@@ -4759,6 +4897,7 @@
4759
4897
  "name": "ProjectDeliveryEvent",
4760
4898
  "qualifiedName": "@happyvertical/smrt-projects:ProjectDeliveryEvent",
4761
4899
  "collection": "projectdeliveryevents",
4900
+ "description": "Ordered, idempotent delivery update emitted by the control plane.",
4762
4901
  "tableName": "project_delivery_events",
4763
4902
  "packageName": "@happyvertical/smrt-projects",
4764
4903
  "extends": "SmrtObject",
@@ -4872,6 +5011,7 @@
4872
5011
  "name": "PreviewApproval",
4873
5012
  "qualifiedName": "@happyvertical/smrt-projects:PreviewApproval",
4874
5013
  "collection": "previewapprovals",
5014
+ "description": "Client decision state for one preview produced during delivery.",
4875
5015
  "tableName": "preview_approvals",
4876
5016
  "packageName": "@happyvertical/smrt-projects",
4877
5017
  "extends": "SmrtObject",
@@ -4969,6 +5109,7 @@
4969
5109
  "name": "AssistanceRequest",
4970
5110
  "qualifiedName": "@happyvertical/smrt-projects:AssistanceRequest",
4971
5111
  "collection": "assistancerequests",
5112
+ "description": "Lossless conversational intake before support/development classification.",
4972
5113
  "tableName": "assistance_requests",
4973
5114
  "packageName": "@happyvertical/smrt-projects",
4974
5115
  "extends": "SmrtObject",
@@ -5079,6 +5220,7 @@
5079
5220
  "name": "AssistanceRequestEvent",
5080
5221
  "qualifiedName": "@happyvertical/smrt-projects:AssistanceRequestEvent",
5081
5222
  "collection": "assistancerequestevents",
5223
+ "description": "Append-only explanation of Assistance Request routing decisions.",
5082
5224
  "tableName": "assistance_request_events",
5083
5225
  "packageName": "@happyvertical/smrt-projects",
5084
5226
  "extends": "SmrtObject",
@@ -5248,6 +5390,14 @@
5248
5390
  }
5249
5391
  ],
5250
5392
  "surfaces": [
5393
+ {
5394
+ "kind": "api",
5395
+ "name": "issues.findByProject",
5396
+ "operation": "findByProject",
5397
+ "objectName": "@happyvertical/smrt-projects:IssueCollection",
5398
+ "path": "/issues/findByProject",
5399
+ "method": "POST"
5400
+ },
5251
5401
  {
5252
5402
  "kind": "api",
5253
5403
  "name": "issues.findByRepository",
@@ -5334,6 +5484,12 @@
5334
5484
  "operation": "discover",
5335
5485
  "objectName": "@happyvertical/smrt-projects:IssueCollection"
5336
5486
  },
5487
+ {
5488
+ "kind": "cli",
5489
+ "name": "issuecollection_findByProject",
5490
+ "operation": "findByProject",
5491
+ "objectName": "@happyvertical/smrt-projects:IssueCollection"
5492
+ },
5337
5493
  {
5338
5494
  "kind": "cli",
5339
5495
  "name": "issuecollection_findByRepository",
@@ -5406,6 +5562,12 @@
5406
5562
  "operation": "discover",
5407
5563
  "objectName": "@happyvertical/smrt-projects:IssueCollection"
5408
5564
  },
5565
+ {
5566
+ "kind": "mcp",
5567
+ "name": "issuecollection_findbyproject",
5568
+ "operation": "findByProject",
5569
+ "objectName": "@happyvertical/smrt-projects:IssueCollection"
5570
+ },
5409
5571
  {
5410
5572
  "kind": "mcp",
5411
5573
  "name": "issuecollection_findbyrepository",
@@ -6747,12 +6909,165 @@
6747
6909
  }
6748
6910
  ],
6749
6911
  "relationshipsV2": {
6750
- "foreignKeyFields": 13,
6912
+ "foreignKeyFields": 16,
6751
6913
  "crossPackageRefFields": 0,
6752
6914
  "junctionCollections": 0,
6753
6915
  "hierarchicalObjects": 0,
6754
6916
  "polymorphicAssociations": 0,
6755
- "uuidColumns": 79
6917
+ "uuidColumns": 87
6756
6918
  },
6757
- "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` (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` (FK), `name`, `color` | |\n\n## Key Patterns\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"
6919
+ "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",
6920
+ "recipes": [
6921
+ {
6922
+ "id": "projects.tracker",
6923
+ "className": "ProjectTrackerRecipe",
6924
+ "label": "Projects",
6925
+ "summary": "Track projects and the issues, labels and comments inside them.",
6926
+ "synonyms": [
6927
+ "issues",
6928
+ "tasks",
6929
+ "tickets",
6930
+ "bugs",
6931
+ "kanban"
6932
+ ],
6933
+ "models": [
6934
+ "@happyvertical/smrt-projects:Project",
6935
+ "@happyvertical/smrt-projects:Issue",
6936
+ "@happyvertical/smrt-projects:Label",
6937
+ "@happyvertical/smrt-projects:Comment"
6938
+ ],
6939
+ "nav": [
6940
+ {
6941
+ "label": "Projects",
6942
+ "model": "@happyvertical/smrt-projects:Project",
6943
+ "icon": "folder",
6944
+ "description": "Bigger jobs broken into steps you can follow."
6945
+ },
6946
+ {
6947
+ "label": "Issues",
6948
+ "model": "@happyvertical/smrt-projects:Issue",
6949
+ "icon": "wrench",
6950
+ "description": "Problems and to-dos to track until they are done."
6951
+ },
6952
+ {
6953
+ "label": "Labels",
6954
+ "model": "@happyvertical/smrt-projects:Label",
6955
+ "icon": "tag",
6956
+ "description": "Tags to sort your projects and issues your way."
6957
+ }
6958
+ ],
6959
+ "requires": [],
6960
+ "section": {
6961
+ "id": "projects",
6962
+ "label": "Projects",
6963
+ "icon": "briefcase",
6964
+ "description": "Projects, their issues, labels and comments."
6965
+ },
6966
+ "options": {
6967
+ "@happyvertical/smrt-projects:Project": {
6968
+ "fields": {
6969
+ "projectId": {
6970
+ "visibility": "hidden"
6971
+ },
6972
+ "projectNumber": {
6973
+ "visibility": "hidden"
6974
+ },
6975
+ "providerType": {
6976
+ "visibility": "hidden"
6977
+ },
6978
+ "tokenConfigKey": {
6979
+ "visibility": "hidden"
6980
+ },
6981
+ "statuses": {
6982
+ "visibility": "hidden"
6983
+ },
6984
+ "fields": {
6985
+ "visibility": "hidden"
6986
+ },
6987
+ "statusFieldId": {
6988
+ "visibility": "hidden"
6989
+ },
6990
+ "statusOptions": {
6991
+ "visibility": "hidden"
6992
+ },
6993
+ "lastSyncedAt": {
6994
+ "visibility": "hidden"
6995
+ }
6996
+ }
6997
+ },
6998
+ "@happyvertical/smrt-projects:Issue": {
6999
+ "fields": {
7000
+ "nodeId": {
7001
+ "visibility": "hidden"
7002
+ },
7003
+ "originalBody": {
7004
+ "visibility": "hidden"
7005
+ },
7006
+ "synthesisCount": {
7007
+ "visibility": "hidden"
7008
+ },
7009
+ "lastSyncedAt": {
7010
+ "visibility": "hidden"
7011
+ },
7012
+ "commentsCount": {
7013
+ "visibility": "hidden"
7014
+ },
7015
+ "repositoryId": {
7016
+ "visibility": "hidden"
7017
+ },
7018
+ "number": {
7019
+ "visibility": "hidden"
7020
+ }
7021
+ }
7022
+ },
7023
+ "@happyvertical/smrt-projects:Comment": {
7024
+ "fields": {
7025
+ "commentId": {
7026
+ "visibility": "hidden"
7027
+ },
7028
+ "url": {
7029
+ "visibility": "hidden"
7030
+ }
7031
+ }
7032
+ },
7033
+ "@happyvertical/smrt-projects:Label": {
7034
+ "fields": {
7035
+ "repositoryId": {
7036
+ "visibility": "hidden"
7037
+ }
7038
+ }
7039
+ }
7040
+ },
7041
+ "help": {
7042
+ "markdown": "## Overview\n\nProjects tracks the work you are planning: projects, the issues in them, and\nthe labels and comments that organise them.\n\n## Tasks\n\n### Start a project\n\n1. Create a new project.\n2. Give it a **{field:title}** and a **{field:description}**.\n\n### Log an issue\n\n1. Create a new issue and pick the **{field:projectId}**.\n2. Write a **{field:title}** and a **{field:body}**.\n3. Close it by setting **{field:state}** to closed. Comments are attached to the\n issue.\n",
7043
+ "fieldRefs": [
7044
+ "body",
7045
+ "description",
7046
+ "projectId",
7047
+ "state",
7048
+ "title"
7049
+ ]
7050
+ },
7051
+ "demo": {
7052
+ "mode": "server",
7053
+ "reasons": [
7054
+ "Needs a server because its package does not build for a browser: node:crypto/async_hooks; core migrations; repos and ai SDKs; and its package depends on @happyvertical/smrt-subscriptions, @happyvertical/smrt-tenancy, @happyvertical/smrt-timesheets, which do not build for a browser (#3624, #3626, #3627)."
7055
+ ]
7056
+ }
7057
+ }
7058
+ ],
7059
+ "browser": {
7060
+ "status": "server-only",
7061
+ "issues": [
7062
+ "#3624",
7063
+ "#3626",
7064
+ "#3627"
7065
+ ],
7066
+ "reason": "node:crypto/async_hooks; core migrations; repos and ai SDKs",
7067
+ "via": [
7068
+ "@happyvertical/smrt-subscriptions",
7069
+ "@happyvertical/smrt-tenancy",
7070
+ "@happyvertical/smrt-timesheets"
7071
+ ]
7072
+ }
6758
7073
  }