@intentius/chant 0.100.0 → 0.101.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build.d.ts +6 -0
- package/dist/build.d.ts.map +1 -1
- package/dist/cli/build-options.d.ts +2 -0
- package/dist/cli/build-options.d.ts.map +1 -1
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/import.d.ts.map +1 -1
- package/dist/cli/handlers/fan-out.d.ts.map +1 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/mcp/workspace-tools.d.ts +8 -0
- package/dist/cli/mcp/workspace-tools.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +22 -0
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/config.d.ts +11 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/lexicon.d.ts +24 -1
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
- package/dist/lifecycle/plan-digest.d.ts +26 -5
- package/dist/lifecycle/plan-digest.d.ts.map +1 -1
- package/dist/lint/config.d.ts +4 -4
- package/dist/op/activities/activity-contracts.d.ts +1 -0
- package/dist/op/activities/activity-contracts.d.ts.map +1 -1
- package/dist/op/activities/propose-upgrade.d.ts +2 -0
- package/dist/op/activities/propose-upgrade.d.ts.map +1 -1
- package/dist/op/index.d.ts +1 -1
- package/dist/op/index.d.ts.map +1 -1
- package/dist/serializer.d.ts +8 -0
- package/dist/serializer.d.ts.map +1 -1
- package/dist/telemetry-attribution.d.ts +77 -0
- package/dist/telemetry-attribution.d.ts.map +1 -0
- package/dist/workspace/agent-cli.d.ts +83 -0
- package/dist/workspace/agent-cli.d.ts.map +1 -0
- package/dist/workspace/changes-cli.d.ts.map +1 -1
- package/dist/workspace/changes.d.ts +8 -1
- package/dist/workspace/changes.d.ts.map +1 -1
- package/dist/workspace/checks/links.d.ts +1 -0
- package/dist/workspace/checks/links.d.ts.map +1 -1
- package/dist/workspace/checks/live.d.ts +40 -0
- package/dist/workspace/checks/live.d.ts.map +1 -0
- package/dist/workspace/checks.d.ts +21 -2
- package/dist/workspace/checks.d.ts.map +1 -1
- package/dist/workspace/compose-graph.d.ts +63 -0
- package/dist/workspace/compose-graph.d.ts.map +1 -1
- package/dist/workspace/decide.d.ts +1 -1
- package/dist/workspace/decide.d.ts.map +1 -1
- package/dist/workspace/declaration.d.ts +32 -0
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.schema.json +138 -3
- package/dist/workspace/export-cli.d.ts +12 -0
- package/dist/workspace/export-cli.d.ts.map +1 -0
- package/dist/workspace/export.d.ts +145 -0
- package/dist/workspace/export.d.ts.map +1 -0
- package/dist/workspace/graph-cli.d.ts.map +1 -1
- package/dist/workspace/import.d.ts +73 -0
- package/dist/workspace/import.d.ts.map +1 -0
- package/dist/workspace/kinds.d.ts +6 -2
- package/dist/workspace/kinds.d.ts.map +1 -1
- package/dist/workspace/lineage-adopt-cli.d.ts +15 -0
- package/dist/workspace/lineage-adopt-cli.d.ts.map +1 -0
- package/dist/workspace/lineage-adopt.d.ts +106 -0
- package/dist/workspace/lineage-adopt.d.ts.map +1 -0
- package/dist/workspace/lineage-check.d.ts +9 -2
- package/dist/workspace/lineage-check.d.ts.map +1 -1
- package/dist/workspace/lineage-cli.d.ts +6 -1
- package/dist/workspace/lineage-cli.d.ts.map +1 -1
- package/dist/workspace/lineage-hash-index.d.ts +110 -0
- package/dist/workspace/lineage-hash-index.d.ts.map +1 -0
- package/dist/workspace/lineage-init.d.ts +10 -0
- package/dist/workspace/lineage-init.d.ts.map +1 -1
- package/dist/workspace/lineage-lock.d.ts +147 -0
- package/dist/workspace/lineage-lock.d.ts.map +1 -1
- package/dist/workspace/lineage-migrations.d.ts +15 -3
- package/dist/workspace/lineage-migrations.d.ts.map +1 -1
- package/dist/workspace/lineage-provenance.d.ts +18 -0
- package/dist/workspace/lineage-provenance.d.ts.map +1 -0
- package/dist/workspace/lineage-upgrade-cli.d.ts +2 -0
- package/dist/workspace/lineage-upgrade-cli.d.ts.map +1 -1
- package/dist/workspace/lineage-upgrade.d.ts +30 -1
- package/dist/workspace/lineage-upgrade.d.ts.map +1 -1
- package/dist/workspace/lineage-versions.d.ts +116 -0
- package/dist/workspace/lineage-versions.d.ts.map +1 -0
- package/dist/workspace/links.d.ts +31 -5
- package/dist/workspace/links.d.ts.map +1 -1
- package/dist/workspace/ls-generated.d.ts +37 -0
- package/dist/workspace/ls-generated.d.ts.map +1 -0
- package/dist/workspace/ls.d.ts +4 -0
- package/dist/workspace/ls.d.ts.map +1 -1
- package/dist/workspace/member-commands.d.ts.map +1 -1
- package/dist/workspace/nested-graph.d.ts +56 -0
- package/dist/workspace/nested-graph.d.ts.map +1 -0
- package/dist/workspace/nesting.d.ts +21 -0
- package/dist/workspace/nesting.d.ts.map +1 -0
- package/dist/workspace/pin-cli.d.ts +10 -0
- package/dist/workspace/pin-cli.d.ts.map +1 -0
- package/dist/workspace/pin-integrity.d.ts +51 -0
- package/dist/workspace/pin-integrity.d.ts.map +1 -0
- package/dist/workspace/reason-codes.d.ts +26 -2
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/record-sessions.d.ts +7 -11
- package/dist/workspace/record-sessions.d.ts.map +1 -1
- package/dist/workspace/records-cli.d.ts +30 -1
- package/dist/workspace/records-cli.d.ts.map +1 -1
- package/dist/workspace/records-close.d.ts +5 -2
- package/dist/workspace/records-close.d.ts.map +1 -1
- package/dist/workspace/records-write.d.ts +22 -4
- package/dist/workspace/records-write.d.ts.map +1 -1
- package/dist/workspace/records.d.ts +43 -5
- package/dist/workspace/records.d.ts.map +1 -1
- package/dist/workspace/returns.d.ts +129 -0
- package/dist/workspace/returns.d.ts.map +1 -0
- package/dist/workspace/status-gates.d.ts.map +1 -1
- package/dist/workspace/template-manifest.d.ts +11 -3
- package/dist/workspace/template-manifest.d.ts.map +1 -1
- package/dist/workspace/trust/attestor.d.ts +8 -0
- package/dist/workspace/trust/attestor.d.ts.map +1 -1
- package/dist/workspace/trust/dsse.d.ts +58 -0
- package/dist/workspace/trust/dsse.d.ts.map +1 -0
- package/dist/workspace/trust/evidence-cli.d.ts +66 -0
- package/dist/workspace/trust/evidence-cli.d.ts.map +1 -0
- package/dist/workspace/trust/evidence.d.ts +93 -0
- package/dist/workspace/trust/evidence.d.ts.map +1 -0
- package/dist/workspace/trust/policy.d.ts +54 -1
- package/dist/workspace/trust/policy.d.ts.map +1 -1
- package/dist/workspace/trust/provenance.d.ts +21 -1
- package/dist/workspace/trust/provenance.d.ts.map +1 -1
- package/dist/workspace/trust/rotation.d.ts +132 -0
- package/dist/workspace/trust/rotation.d.ts.map +1 -0
- package/dist/workspace/trust/seal.d.ts.map +1 -1
- package/dist/workspace/trust/signers-cli.d.ts +49 -0
- package/dist/workspace/trust/signers-cli.d.ts.map +1 -0
- package/dist/workspace/trust/ssh-commit.d.ts +12 -0
- package/dist/workspace/trust/ssh-commit.d.ts.map +1 -1
- package/dist/workspace/trust/test-repo.d.ts +13 -0
- package/dist/workspace/trust/test-repo.d.ts.map +1 -1
- package/dist/workspace/trust/verify.d.ts +15 -0
- package/dist/workspace/trust/verify.d.ts.map +1 -1
- package/dist/workspace/work-evidence.d.ts +1 -1
- package/dist/workspace/work-evidence.d.ts.map +1 -1
- package/dist/workspace/write-scope.d.ts +199 -0
- package/dist/workspace/write-scope.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/build.ts +8 -0
- package/src/cli/build-options.ts +4 -1
- package/src/cli/commands/build.ts +2 -0
- package/src/cli/commands/import-live.test.ts +69 -1
- package/src/cli/commands/import.ts +48 -22
- package/src/cli/handlers/fan-out.test.ts +6 -6
- package/src/cli/handlers/fan-out.ts +2 -1
- package/src/cli/handlers/graph.test.ts +42 -0
- package/src/cli/handlers/graph.ts +22 -0
- package/src/cli/handlers/operator.ts +1 -1
- package/src/cli/main.test.ts +32 -0
- package/src/cli/main.ts +103 -6
- package/src/cli/mcp/workspace-tools.test.ts +1 -1
- package/src/cli/mcp/workspace-tools.ts +33 -2
- package/src/cli/registry.ts +22 -0
- package/src/cli/serve-mcp-workspace.test.ts +1 -1
- package/src/codegen/release-wiring.test.ts +5 -1
- package/src/components/fan-out-output.test.ts +1 -1
- package/src/components/fan-out.test.ts +1 -1
- package/src/components/promote.test.ts +1 -1
- package/src/config.ts +12 -0
- package/src/content-digest.test.ts +2 -2
- package/src/lexicon.ts +25 -1
- package/src/lifecycle/gate-ledger.test.ts +14 -0
- package/src/lifecycle/gate-ledger.ts +2 -1
- package/src/lifecycle/plan-digest.test.ts +54 -3
- package/src/lifecycle/plan-digest.ts +38 -8
- package/src/op/activities/activity-contracts.ts +1 -0
- package/src/op/activities/propose-upgrade.ts +8 -5
- package/src/op/gate-approval.test.ts +17 -0
- package/src/op/gate.ts +3 -3
- package/src/op/index.ts +1 -1
- package/src/serializer.ts +9 -0
- package/src/telemetry-attribution.test.ts +91 -0
- package/src/telemetry-attribution.ts +145 -0
- package/src/workspace/agent-cli.ts +134 -0
- package/src/workspace/agent.schema.json +356 -0
- package/src/workspace/behold-kinds.test.ts +1 -1
- package/src/workspace/changes-cli.ts +5 -0
- package/src/workspace/changes.schema.json +179 -1
- package/src/workspace/changes.ts +58 -4
- package/src/workspace/check-live.test.ts +192 -0
- package/src/workspace/check.schema.json +64 -0
- package/src/workspace/checks/links.ts +23 -2
- package/src/workspace/checks/live.ts +113 -0
- package/src/workspace/checks.test.ts +2 -2
- package/src/workspace/checks.ts +20 -3
- package/src/workspace/compose-graph.test.ts +47 -0
- package/src/workspace/compose-graph.ts +120 -3
- package/src/workspace/conformance/__fixture__/decisions/decision.kind.mjs +7 -1
- package/src/workspace/conformance/__fixture__/decisions/decision.schema.json +5 -0
- package/src/workspace/declaration.schema.json +138 -3
- package/src/workspace/declaration.ts +106 -1
- package/src/workspace/declared-kinds.test.ts +33 -0
- package/src/workspace/evidence.schema.json +279 -0
- package/src/workspace/export-cli.ts +180 -0
- package/src/workspace/export-import.test.ts +282 -0
- package/src/workspace/export.ts +486 -0
- package/src/workspace/graph-cli.ts +47 -1
- package/src/workspace/graph-contract.test.ts +89 -4
- package/src/workspace/graph.schema.json +206 -1
- package/src/workspace/import.ts +325 -0
- package/src/workspace/kinds.test.ts +5 -5
- package/src/workspace/kinds.ts +25 -3
- package/src/workspace/lineage-adopt-cli.ts +103 -0
- package/src/workspace/lineage-adopt.test.ts +552 -0
- package/src/workspace/lineage-adopt.ts +452 -0
- package/src/workspace/lineage-check.ts +26 -5
- package/src/workspace/lineage-cli.ts +12 -2
- package/src/workspace/lineage-hash-index.ts +305 -0
- package/src/workspace/lineage-init.test.ts +9 -0
- package/src/workspace/lineage-init.ts +29 -9
- package/src/workspace/lineage-lock.ts +54 -0
- package/src/workspace/lineage-migrations.test.ts +27 -0
- package/src/workspace/lineage-migrations.ts +44 -14
- package/src/workspace/lineage-provenance.ts +40 -0
- package/src/workspace/lineage-upgrade-cli.ts +9 -5
- package/src/workspace/lineage-upgrade.test.ts +92 -1
- package/src/workspace/lineage-upgrade.ts +111 -16
- package/src/workspace/lineage-versions.ts +348 -0
- package/src/workspace/links.test.ts +121 -2
- package/src/workspace/links.ts +97 -6
- package/src/workspace/ls-contract.test.ts +84 -1
- package/src/workspace/ls-generated.ts +111 -0
- package/src/workspace/ls.schema.json +19 -0
- package/src/workspace/ls.ts +8 -1
- package/src/workspace/member-commands.ts +3 -1
- package/src/workspace/nested-graph.test.ts +176 -0
- package/src/workspace/nested-graph.ts +169 -0
- package/src/workspace/nesting.ts +37 -0
- package/src/workspace/pin-cli.test.ts +71 -0
- package/src/workspace/pin-cli.ts +57 -0
- package/src/workspace/pin-integrity.test.ts +121 -0
- package/src/workspace/pin-integrity.ts +104 -0
- package/src/workspace/points-write.schema.json +1 -0
- package/src/workspace/read-contract.test.ts +24 -0
- package/src/workspace/reason-codes.test.ts +10 -0
- package/src/workspace/reason-codes.ts +29 -2
- package/src/workspace/record-sessions.ts +12 -14
- package/src/workspace/records-amend.schema.json +4 -0
- package/src/workspace/records-cli.ts +93 -12
- package/src/workspace/records-close.schema.json +6 -2
- package/src/workspace/records-close.ts +10 -2
- package/src/workspace/records-formats.test.ts +13 -5
- package/src/workspace/records-new.schema.json +4 -0
- package/src/workspace/records-review.schema.json +4 -0
- package/src/workspace/records-sessions.test.ts +23 -6
- package/src/workspace/records-write.test.ts +65 -8
- package/src/workspace/records-write.ts +70 -6
- package/src/workspace/records.schema.json +55 -3
- package/src/workspace/records.ts +96 -5
- package/src/workspace/returns.ts +328 -0
- package/src/workspace/signers.schema.json +206 -0
- package/src/workspace/status-gates.ts +2 -1
- package/src/workspace/template-manifest.ts +22 -4
- package/src/workspace/trust/attestor.ts +15 -0
- package/src/workspace/trust/dsse.ts +134 -0
- package/src/workspace/trust/evidence-cli.ts +195 -0
- package/src/workspace/trust/evidence.test.ts +241 -0
- package/src/workspace/trust/evidence.ts +207 -0
- package/src/workspace/trust/policy.ts +110 -3
- package/src/workspace/trust/provenance.ts +41 -4
- package/src/workspace/trust/record-seal.test.ts +1 -1
- package/src/workspace/trust/rotation.test.ts +258 -0
- package/src/workspace/trust/rotation.ts +336 -0
- package/src/workspace/trust/seal.ts +11 -0
- package/src/workspace/trust/signers-cli.ts +178 -0
- package/src/workspace/trust/ssh-commit.ts +53 -4
- package/src/workspace/trust/test-repo.ts +18 -0
- package/src/workspace/trust/trust.test.ts +8 -2
- package/src/workspace/trust/verify-cli.ts +1 -0
- package/src/workspace/trust/verify.ts +22 -0
- package/src/workspace/work-evidence.schema.json +4 -0
- package/src/workspace/write-scope.test.ts +340 -0
- package/src/workspace/write-scope.ts +448 -0
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"paths",
|
|
29
29
|
"findings",
|
|
30
30
|
"summary",
|
|
31
|
+
"scope",
|
|
31
32
|
"ok"
|
|
32
33
|
],
|
|
33
34
|
"properties": {
|
|
@@ -198,9 +199,20 @@
|
|
|
198
199
|
}
|
|
199
200
|
}
|
|
200
201
|
},
|
|
202
|
+
"scope": {
|
|
203
|
+
"description": "The write scope check (#2548, ws-067): each commit in the range, merges left out, judged against the writeScope and agents of the declaration at the range's base, with the trust policy read there. Null when the base has no declaration, or one with no writeScope block and no agents. Every finding fails the check, whatever severity says.",
|
|
204
|
+
"oneOf": [
|
|
205
|
+
{
|
|
206
|
+
"type": "null"
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
"$ref": "#/$defs/scope"
|
|
210
|
+
}
|
|
211
|
+
]
|
|
212
|
+
},
|
|
201
213
|
"ok": {
|
|
202
214
|
"type": "boolean",
|
|
203
|
-
"description": "False
|
|
215
|
+
"description": "False when severity is fail and there is a finding, or when scope has a finding."
|
|
204
216
|
}
|
|
205
217
|
}
|
|
206
218
|
},
|
|
@@ -467,6 +479,172 @@
|
|
|
467
479
|
},
|
|
468
480
|
"paths": false
|
|
469
481
|
}
|
|
482
|
+
},
|
|
483
|
+
"scope": {
|
|
484
|
+
"type": "object",
|
|
485
|
+
"required": [
|
|
486
|
+
"base",
|
|
487
|
+
"restricted",
|
|
488
|
+
"agents",
|
|
489
|
+
"commits",
|
|
490
|
+
"findings"
|
|
491
|
+
],
|
|
492
|
+
"properties": {
|
|
493
|
+
"base": {
|
|
494
|
+
"type": "string",
|
|
495
|
+
"pattern": "^[0-9a-f]{40,64}$",
|
|
496
|
+
"description": "The commit the declaration and the trust policy were read at: the range's base."
|
|
497
|
+
},
|
|
498
|
+
"restricted": {
|
|
499
|
+
"type": "array",
|
|
500
|
+
"items": {
|
|
501
|
+
"enum": [
|
|
502
|
+
"human",
|
|
503
|
+
"agent",
|
|
504
|
+
"runner",
|
|
505
|
+
"service"
|
|
506
|
+
]
|
|
507
|
+
},
|
|
508
|
+
"description": "The principal classes with a writeScope entry, and agent when any agent session is declared."
|
|
509
|
+
},
|
|
510
|
+
"agents": {
|
|
511
|
+
"type": "array",
|
|
512
|
+
"items": {
|
|
513
|
+
"type": "string"
|
|
514
|
+
},
|
|
515
|
+
"description": "The agent sessions the declaration at base names."
|
|
516
|
+
},
|
|
517
|
+
"commits": {
|
|
518
|
+
"type": "array",
|
|
519
|
+
"items": {
|
|
520
|
+
"$ref": "#/$defs/scopeCommit"
|
|
521
|
+
}
|
|
522
|
+
},
|
|
523
|
+
"findings": {
|
|
524
|
+
"type": "array",
|
|
525
|
+
"items": {
|
|
526
|
+
"$ref": "#/$defs/scopeFinding"
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
},
|
|
531
|
+
"scopeCommit": {
|
|
532
|
+
"type": "object",
|
|
533
|
+
"required": [
|
|
534
|
+
"commit",
|
|
535
|
+
"subject",
|
|
536
|
+
"principal",
|
|
537
|
+
"attested",
|
|
538
|
+
"class",
|
|
539
|
+
"agent",
|
|
540
|
+
"paths"
|
|
541
|
+
],
|
|
542
|
+
"properties": {
|
|
543
|
+
"commit": {
|
|
544
|
+
"type": "string",
|
|
545
|
+
"pattern": "^[0-9a-f]{40,64}$"
|
|
546
|
+
},
|
|
547
|
+
"subject": {
|
|
548
|
+
"type": "string"
|
|
549
|
+
},
|
|
550
|
+
"principal": {
|
|
551
|
+
"type": "string",
|
|
552
|
+
"description": "The signer that attests the commit under the policy at base, or else the author's email, which the commit only claims."
|
|
553
|
+
},
|
|
554
|
+
"attested": {
|
|
555
|
+
"type": "boolean",
|
|
556
|
+
"description": "Whether principal is an attested signer."
|
|
557
|
+
},
|
|
558
|
+
"class": {
|
|
559
|
+
"enum": [
|
|
560
|
+
"human",
|
|
561
|
+
"agent",
|
|
562
|
+
"runner",
|
|
563
|
+
"service"
|
|
564
|
+
],
|
|
565
|
+
"description": "agent for a commit naming a session with a Chant-Agent trailer or by a principal a session lists; otherwise the class the principal's role grants at base give."
|
|
566
|
+
},
|
|
567
|
+
"agent": {
|
|
568
|
+
"type": [
|
|
569
|
+
"string",
|
|
570
|
+
"null"
|
|
571
|
+
],
|
|
572
|
+
"description": "The agent session, or null."
|
|
573
|
+
},
|
|
574
|
+
"paths": {
|
|
575
|
+
"type": "integer",
|
|
576
|
+
"minimum": 0,
|
|
577
|
+
"description": "How many paths in the workspace the commit writes."
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
},
|
|
581
|
+
"scopeFinding": {
|
|
582
|
+
"type": "object",
|
|
583
|
+
"required": [
|
|
584
|
+
"id",
|
|
585
|
+
"code",
|
|
586
|
+
"commit",
|
|
587
|
+
"path",
|
|
588
|
+
"principal",
|
|
589
|
+
"class",
|
|
590
|
+
"agent",
|
|
591
|
+
"verb",
|
|
592
|
+
"message"
|
|
593
|
+
],
|
|
594
|
+
"properties": {
|
|
595
|
+
"id": {
|
|
596
|
+
"type": "string"
|
|
597
|
+
},
|
|
598
|
+
"code": {
|
|
599
|
+
"enum": [
|
|
600
|
+
"write-scope-member",
|
|
601
|
+
"write-scope-kind",
|
|
602
|
+
"agent-unknown"
|
|
603
|
+
]
|
|
604
|
+
},
|
|
605
|
+
"commit": {
|
|
606
|
+
"type": "string",
|
|
607
|
+
"pattern": "^[0-9a-f]{40,64}$"
|
|
608
|
+
},
|
|
609
|
+
"path": {
|
|
610
|
+
"type": [
|
|
611
|
+
"string",
|
|
612
|
+
"null"
|
|
613
|
+
],
|
|
614
|
+
"description": "From the workspace root; null for agent-unknown, a finding on the whole commit."
|
|
615
|
+
},
|
|
616
|
+
"principal": {
|
|
617
|
+
"type": "string"
|
|
618
|
+
},
|
|
619
|
+
"class": {
|
|
620
|
+
"enum": [
|
|
621
|
+
"human",
|
|
622
|
+
"agent",
|
|
623
|
+
"runner",
|
|
624
|
+
"service"
|
|
625
|
+
]
|
|
626
|
+
},
|
|
627
|
+
"agent": {
|
|
628
|
+
"type": [
|
|
629
|
+
"string",
|
|
630
|
+
"null"
|
|
631
|
+
]
|
|
632
|
+
},
|
|
633
|
+
"verb": {
|
|
634
|
+
"enum": [
|
|
635
|
+
"new",
|
|
636
|
+
"amend",
|
|
637
|
+
"review",
|
|
638
|
+
"close",
|
|
639
|
+
"delete",
|
|
640
|
+
null
|
|
641
|
+
],
|
|
642
|
+
"description": "For a record file of a kind the declaration at base names: how the commit wrote it. A modified record is review when only its reviews (or a session's verdicts) changed, close when a session entered a closed state, and amend otherwise."
|
|
643
|
+
},
|
|
644
|
+
"message": {
|
|
645
|
+
"type": "string"
|
|
646
|
+
}
|
|
647
|
+
}
|
|
470
648
|
}
|
|
471
649
|
}
|
|
472
650
|
}
|
package/src/workspace/changes.ts
CHANGED
|
@@ -40,17 +40,18 @@ import { execFileSync } from "node:child_process";
|
|
|
40
40
|
import { relative, resolve, sep } from "node:path";
|
|
41
41
|
// @ts-ignore — picomatch has no types declaration
|
|
42
42
|
import picomatch from "picomatch";
|
|
43
|
-
import { declaredRecordKinds, readDeclaration, readerVersion, WORKSPACE_ERROR_CODES, WorkspaceReadError, type ChangeSeverity } from "./declaration";
|
|
43
|
+
import { declaredRecordKinds, readDeclaration, readerVersion, WORKSPACE_ERROR_CODES, WorkspaceReadError, type ChangeSeverity, type Declaration } from "./declaration";
|
|
44
44
|
import { declaredKindFile } from "./declared-kinds";
|
|
45
45
|
import { isGeneratedPath } from "./generated-files";
|
|
46
46
|
import { parseRegion } from "./intent";
|
|
47
47
|
import { constraintCovers, isWorkspacePath, memberHolding } from "./record-assets";
|
|
48
|
-
import { RecordReadError, type LoadedRecordKind, type RecordEntry } from "./records";
|
|
48
|
+
import { loadRecordKind, RecordReadError, type LoadedRecordKind, type RecordEntry } from "./records";
|
|
49
49
|
import { readRecordsFor } from "./records-cli";
|
|
50
50
|
import type { ReasonCode } from "./reason-codes";
|
|
51
51
|
import { joinPath } from "./tree";
|
|
52
52
|
import { locateWorkspace } from "./which-chant";
|
|
53
53
|
import { idList, isDecided } from "./work";
|
|
54
|
+
import { checkWriteScope, type CheckKind, type ScopeReport } from "./write-scope";
|
|
54
55
|
|
|
55
56
|
/** The version of the `changes` document this chant writes. */
|
|
56
57
|
export const CHANGES_CONTRACT_VERSION = 1;
|
|
@@ -172,7 +173,13 @@ export type ChangesDocument =
|
|
|
172
173
|
paths: ChangedPath[];
|
|
173
174
|
findings: ChangeFinding[];
|
|
174
175
|
summary: { paths: number; covered: number; uncovered: number; outOfScope: number; ignored: number; records: number };
|
|
175
|
-
/**
|
|
176
|
+
/**
|
|
177
|
+
* The write scope check (#2548): each commit in the range judged
|
|
178
|
+
* against the writeScope and agents the declaration at base gives,
|
|
179
|
+
* or null when that declaration restricts no one.
|
|
180
|
+
*/
|
|
181
|
+
scope: ScopeReport | null;
|
|
182
|
+
/** False when severity is fail and there are findings, or when a commit writes outside its writer's scope. */
|
|
176
183
|
ok: boolean;
|
|
177
184
|
})
|
|
178
185
|
| (Head & { error: { code: ChangesErrorCode; message: string } });
|
|
@@ -463,6 +470,7 @@ async function run(query: ChangesQuery, head: Head): Promise<Exclude<ChangesDocu
|
|
|
463
470
|
}
|
|
464
471
|
}
|
|
465
472
|
const count = (s: ChangeStatus) => paths.filter((p) => p.status === s).length;
|
|
473
|
+
const scope = await writeScopeAtBase(query, top, range, workspacePrefix);
|
|
466
474
|
return {
|
|
467
475
|
...head,
|
|
468
476
|
workspace: { name: declaration.name, root: located.root },
|
|
@@ -474,10 +482,56 @@ async function run(query: ChangesQuery, head: Head): Promise<Exclude<ChangesDocu
|
|
|
474
482
|
paths,
|
|
475
483
|
findings,
|
|
476
484
|
summary: { paths: paths.length, covered: count("covered"), uncovered: count("uncovered"), outOfScope: count("out-of-scope"), ignored: count("ignored"), records: count("record") },
|
|
477
|
-
|
|
485
|
+
scope,
|
|
486
|
+
ok: !(severity === "fail" && findings.length > 0) && (scope === null || scope.findings.length === 0),
|
|
478
487
|
};
|
|
479
488
|
}
|
|
480
489
|
|
|
490
|
+
/**
|
|
491
|
+
* The write scope check over the range (#2548), with the declaration, its
|
|
492
|
+
* record kinds and the trust policy read at the range's base, so a change
|
|
493
|
+
* can't widen its own scope. Null when the base has no declaration, or one
|
|
494
|
+
* that restricts no one. The base is read without the root-chant rule: a
|
|
495
|
+
* change may move the chant pin, and the scope is data either chant reads.
|
|
496
|
+
*/
|
|
497
|
+
async function writeScopeAtBase(query: ChangesQuery, top: string, range: { base: string; head: string }, prefix: string): Promise<ScopeReport | null> {
|
|
498
|
+
let located: ReturnType<typeof locateWorkspace>;
|
|
499
|
+
let declaration: Declaration;
|
|
500
|
+
try {
|
|
501
|
+
located = locateWorkspace(query.cwd, range.base);
|
|
502
|
+
declaration = readDeclaration(located.tree);
|
|
503
|
+
} catch (err) {
|
|
504
|
+
if (err instanceof WorkspaceReadError && err.code === "declaration-missing") return null;
|
|
505
|
+
throw err;
|
|
506
|
+
}
|
|
507
|
+
if (declaration.writeScope === null && declaration.agents.length === 0) return null;
|
|
508
|
+
const kinds: CheckKind[] = [];
|
|
509
|
+
for (const d of declaredRecordKinds(declaration)) {
|
|
510
|
+
let loaded: LoadedRecordKind;
|
|
511
|
+
try {
|
|
512
|
+
loaded = await loadRecordKind(declaredKindFile(d, located.rootOnDisk), query.cwd);
|
|
513
|
+
} catch (err) {
|
|
514
|
+
// A kind that doesn't load here: its files are judged as plain paths.
|
|
515
|
+
if (err instanceof RecordReadError) continue;
|
|
516
|
+
throw err;
|
|
517
|
+
}
|
|
518
|
+
const { kind } = loaded;
|
|
519
|
+
const session = kind.session;
|
|
520
|
+
kinds.push({
|
|
521
|
+
scoped: { name: kind.name, declaredName: d.name, member: d.member, declared: true },
|
|
522
|
+
dir: toPosix(relative(top, loaded.dir)) || ".",
|
|
523
|
+
match: new RegExp(kind.location.match),
|
|
524
|
+
reviewFields: [kind.reviews?.field, session?.verdicts].filter((f): f is string => typeof f === "string"),
|
|
525
|
+
session:
|
|
526
|
+
session && kind.stateField !== undefined
|
|
527
|
+
? { stateField: kind.stateField, closed: kind.closedStates ?? [], fills: [session.openedRev].filter((f): f is string => typeof f === "string") }
|
|
528
|
+
: null,
|
|
529
|
+
format: kind.format,
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
return checkWriteScope({ top, base: range.base, head: range.head, prefix, declaration, kinds });
|
|
533
|
+
}
|
|
534
|
+
|
|
481
535
|
function finding(code: ChangesFindingCode, p: ChangedPath, severity: "warn" | "fail", message: string, records: string[]): ChangeFinding {
|
|
482
536
|
return { id: `finding:${code}:${p.path}`, code, path: p.path, message, severity, records, triage: changeFindingSource({ code, path: p.path }) };
|
|
483
537
|
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `chant workspace check --live --env <env>` (#2549, ws-062): declared member
|
|
3
|
+
* links resolved against the live graph of each producer. A fake chant stands
|
|
4
|
+
* in for the member's own toolchain: it prints ir.json for a source read and
|
|
5
|
+
* live.json for `--live`, and records its command line in argv.txt.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { afterAll, afterEach, describe, expect, test, vi } from "vitest";
|
|
11
|
+
import { cleanScratch, contract, declaration, repo } from "./__fixtures__/contract-repo";
|
|
12
|
+
import { parseArgs } from "../cli/main";
|
|
13
|
+
import schema from "./check.schema.json";
|
|
14
|
+
import { workspaceGraph } from "./graph-cli";
|
|
15
|
+
import { runChecks, runWorkspaceCheck } from "./lineage-check";
|
|
16
|
+
import type { CheckDocument } from "./lineage-check";
|
|
17
|
+
|
|
18
|
+
const { expectValid } = contract(schema);
|
|
19
|
+
|
|
20
|
+
afterAll(() => cleanScratch());
|
|
21
|
+
afterEach(() => vi.restoreAllMocks());
|
|
22
|
+
|
|
23
|
+
const FAKE_CHANT = `#!/bin/sh
|
|
24
|
+
[ "$1" = graph ] || { echo "Error: Unknown command: $1" >&2; exit 1; }
|
|
25
|
+
printf '%s\\n' "$*" > ../argv.txt
|
|
26
|
+
case " $* " in
|
|
27
|
+
*" --live "*) cat live.json ;;
|
|
28
|
+
*) cat ir.json ;;
|
|
29
|
+
esac
|
|
30
|
+
`;
|
|
31
|
+
|
|
32
|
+
const graph = (exports: { name: string; value?: unknown }[]) => JSON.stringify({ version: 1, nodes: [], edges: [], groups: {}, exports });
|
|
33
|
+
|
|
34
|
+
function workspace(live: { name: string; value?: unknown }[], links: unknown[], producerKind: "chant" | "other" = "chant", extra: Record<string, unknown> = {}) {
|
|
35
|
+
return repo({
|
|
36
|
+
"chant.workspace.json": declaration([
|
|
37
|
+
producerKind === "chant" ? { name: "api", dir: "api", kind: "chant" } : { name: "api", dir: "api", kind: "other", because: "a queue made by hand", outputs: ["QueueUrl"] },
|
|
38
|
+
{ name: "web", dir: "web", kind: "other", because: "a static site", links },
|
|
39
|
+
], extra),
|
|
40
|
+
"api/chant.config.ts": "export default {};\n",
|
|
41
|
+
"api/ir.json": graph([{ name: "QueueUrl" }, { name: "TopicArn" }]),
|
|
42
|
+
"api/live.json": graph(live),
|
|
43
|
+
"web/index.html": "",
|
|
44
|
+
".gitignore": "node_modules\n",
|
|
45
|
+
"node_modules/.bin/chant": { text: FAKE_CHANT, mode: 0o755 },
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function live(root: string, env = "prod") {
|
|
50
|
+
const doc = (await runChecks(root, undefined, { live: { env } })) as Extract<CheckDocument, { workspace: unknown }>;
|
|
51
|
+
const diagnostics = doc.declaration!.diagnostics.filter((d) => d.ruleId === "WSP141" || d.ruleId === "WSP142");
|
|
52
|
+
return { doc, diagnostics, rows: doc.declaration!.live?.links ?? [] };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
describe("check --live (#2549)", () => {
|
|
56
|
+
test("a link to an output the live graph publishes resolves, and the row says it resolved live", async () => {
|
|
57
|
+
const root = workspace([{ name: "QueueUrl", value: "https://q" }], [{ member: "api", output: "QueueUrl" }]);
|
|
58
|
+
const { doc, diagnostics, rows } = await live(root);
|
|
59
|
+
expect(diagnostics).toEqual([]);
|
|
60
|
+
expectValid(doc);
|
|
61
|
+
expect(doc.declaration!.live!.env).toBe("prod");
|
|
62
|
+
expect(rows).toMatchObject([{ consumer: "web", producer: "api", output: "QueueUrl", resolves: "live", status: "resolved", reason: null }]);
|
|
63
|
+
// The member ran as `chant graph --live --env prod`.
|
|
64
|
+
expect(readFileSync(join(root, "argv.txt"), "utf-8")).toContain("--env prod --live");
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("an output the producer declares but its estate does not publish is WSP141, a warning", async () => {
|
|
68
|
+
const root = workspace([{ name: "TopicArn", value: "arn" }], [{ member: "api", output: "QueueUrl" }]);
|
|
69
|
+
const { diagnostics, rows } = await live(root);
|
|
70
|
+
expect(rows[0]).toMatchObject({ status: "missing", resolves: "live" });
|
|
71
|
+
expect(diagnostics).toHaveLength(1);
|
|
72
|
+
expect(diagnostics[0]).toMatchObject({ ruleId: "WSP141", severity: "warning", entity: "web" });
|
|
73
|
+
expect(diagnostics[0].message).toContain("does not resolve live in prod");
|
|
74
|
+
expect(diagnostics[0].message).toContain("TopicArn");
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("a live read that returned no outputs at all leaves the link unresolved, not missing", async () => {
|
|
78
|
+
const root = workspace([], [{ member: "api", output: "QueueUrl" }]);
|
|
79
|
+
const { diagnostics, rows } = await live(root);
|
|
80
|
+
expect(rows[0].status).toBe("unresolved");
|
|
81
|
+
expect(diagnostics).toMatchObject([{ ruleId: "WSP142", severity: "info" }]);
|
|
82
|
+
expect(diagnostics[0].message).toContain("returned no outputs");
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("a producer of a kind chant does not read live is kept unresolved, with the reason", async () => {
|
|
86
|
+
const root = workspace([], [{ member: "api", output: "QueueUrl" }], "other");
|
|
87
|
+
const { diagnostics, rows } = await live(root);
|
|
88
|
+
expect(rows[0]).toMatchObject({ status: "unresolved", resolves: "live" });
|
|
89
|
+
expect(diagnostics).toMatchObject([{ ruleId: "WSP142" }]);
|
|
90
|
+
expect(diagnostics[0].message).toContain("kind other");
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("a telemetry link is not resolved live", async () => {
|
|
94
|
+
const root = workspace([{ name: "QueueUrl" }], [{ member: "api", output: "traces", kind: "telemetry" }]);
|
|
95
|
+
const { rows } = await live(root);
|
|
96
|
+
expect(rows).toEqual([]);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("severity settings apply to the live checks like any other", async () => {
|
|
100
|
+
const root = repo({
|
|
101
|
+
"chant.workspace.json": declaration(
|
|
102
|
+
[
|
|
103
|
+
{ name: "api", dir: "api", kind: "chant" },
|
|
104
|
+
{ name: "web", dir: "web", kind: "other", because: "a static site", links: [{ member: "api", output: "QueueUrl" }], suppress: [{ check: "WSP141", because: "deployed next week" }] },
|
|
105
|
+
],
|
|
106
|
+
{ checks: { WSP141: "error" } },
|
|
107
|
+
),
|
|
108
|
+
"api/chant.config.ts": "export default {};\n",
|
|
109
|
+
"api/ir.json": graph([{ name: "QueueUrl" }]),
|
|
110
|
+
"api/live.json": graph([{ name: "Other" }]),
|
|
111
|
+
"web/index.html": "",
|
|
112
|
+
".gitignore": "node_modules\n",
|
|
113
|
+
"node_modules/.bin/chant": { text: FAKE_CHANT, mode: 0o755 },
|
|
114
|
+
});
|
|
115
|
+
const { doc, diagnostics } = await live(root);
|
|
116
|
+
expect(diagnostics).toEqual([]);
|
|
117
|
+
expect(doc.declaration!.suppressed.map((s) => s.ruleId)).toContain("WSP141");
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test("without --live no member runs and the report has no live section", async () => {
|
|
121
|
+
const root = workspace([{ name: "QueueUrl" }], [{ member: "api", output: "QueueUrl" }]);
|
|
122
|
+
const doc = (await runChecks(root)) as Extract<CheckDocument, { workspace: unknown }>;
|
|
123
|
+
expect(doc.declaration!.live).toBeUndefined();
|
|
124
|
+
expect(existsSync(join(root, "argv.txt"))).toBe(false);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
describe("the command (#2549)", () => {
|
|
129
|
+
async function run(root: string, argv: string[]) {
|
|
130
|
+
const out: string[] = [];
|
|
131
|
+
const err: string[] = [];
|
|
132
|
+
vi.spyOn(console, "log").mockImplementation((...a: unknown[]) => void out.push(a.join(" ")));
|
|
133
|
+
vi.spyOn(console, "error").mockImplementation((...a: unknown[]) => void err.push(a.join(" ")));
|
|
134
|
+
vi.spyOn(process, "cwd").mockReturnValue(root);
|
|
135
|
+
const code = await runWorkspaceCheck({ args: parseArgs(["workspace", "check", ...argv]), plugins: [] } as never);
|
|
136
|
+
return { code, out: out.join("\n"), err: err.join("\n") };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
test("--live needs --env, and takes no --at, and runs no member when it refuses", async () => {
|
|
140
|
+
const root = workspace([{ name: "QueueUrl" }], [{ member: "api", output: "QueueUrl" }]);
|
|
141
|
+
const noEnv = await run(root, ["--live"]);
|
|
142
|
+
expect(noEnv.code).toBe(1);
|
|
143
|
+
expect(noEnv.err).toContain("--live needs an environment");
|
|
144
|
+
const atRev = await run(root, ["--live", "--env", "prod", "--at", "HEAD"]);
|
|
145
|
+
expect(atRev.code).toBe(1);
|
|
146
|
+
expect(atRev.err).toContain("--at");
|
|
147
|
+
expect(existsSync(join(root, "argv.txt"))).toBe(false);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test("--live --json prints the live section, and a WSP141 warning leaves the exit code 0", async () => {
|
|
151
|
+
// The fake producer's source lists no outputs, so the source check WSP093 is turned off; the live warning is the one under test.
|
|
152
|
+
const root = workspace([{ name: "TopicArn" }], [{ member: "api", output: "QueueUrl" }], "chant", { checks: { WSP093: "off" } });
|
|
153
|
+
const { code, out } = await run(root, ["--live", "--env", "prod", "--json"]);
|
|
154
|
+
const report = JSON.parse(out) as { declaration: { live: { env: string; links: { status: string }[] }; diagnostics: { ruleId: string }[] } };
|
|
155
|
+
expect(report.declaration.live).toMatchObject({ env: "prod", links: [{ status: "missing" }] });
|
|
156
|
+
expect(report.declaration.diagnostics.map((d) => d.ruleId)).toContain("WSP141");
|
|
157
|
+
expect(code).toBe(0);
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
describe("the design member kind (#2549, ws-062)", () => {
|
|
162
|
+
const designWorkspace = (extra: Record<string, unknown> = {}) =>
|
|
163
|
+
repo({
|
|
164
|
+
"chant.workspace.json": declaration([
|
|
165
|
+
{ name: "design", dir: "design", kind: "design", outputs: ["screens/home.json"], ...extra },
|
|
166
|
+
{ name: "web", dir: "web", kind: "other", because: "a static site", links: [{ member: "design", output: "screens/home.json" }] },
|
|
167
|
+
]),
|
|
168
|
+
"design/screens/home.json": "{}\n",
|
|
169
|
+
"web/index.html": "",
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test("is a declared kind: no WSP003, no WSP009, no `because` needed, and its listed outputs are link targets", async () => {
|
|
173
|
+
const doc = (await runChecks(designWorkspace())) as Extract<CheckDocument, { workspace: unknown }>;
|
|
174
|
+
const ids = doc.declaration!.diagnostics.map((d) => d.ruleId);
|
|
175
|
+
expect(ids).not.toContain("WSP003");
|
|
176
|
+
expect(doc.declaration!.diagnostics.filter((d) => d.entity === "design")).toEqual([]);
|
|
177
|
+
expect(doc.declaration!.links).toMatchObject([{ consumer: "web", producer: "design", output: "screens/home.json", status: "resolved" }]);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test("a data member: workspace graph reads it and builds nothing, saying so", async () => {
|
|
181
|
+
const { doc } = await workspaceGraph({ cwd: designWorkspace() });
|
|
182
|
+
if ("error" in doc) throw new Error(doc.error.message);
|
|
183
|
+
expect(doc.members.find((m) => m.name === "design")).toMatchObject({ kind: "design", status: "skipped", reason: { code: "kind-not-run" } });
|
|
184
|
+
expect(doc.members.find((m) => m.name === "design")!.reason!.message).toContain("data");
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
test("a design member's link is not resolved live: its files are not an estate", async () => {
|
|
188
|
+
const { rows, diagnostics } = await live(designWorkspace());
|
|
189
|
+
expect(rows[0]).toMatchObject({ status: "unresolved", resolves: "live" });
|
|
190
|
+
expect(diagnostics).toMatchObject([{ ruleId: "WSP142" }]);
|
|
191
|
+
});
|
|
192
|
+
});
|
|
@@ -146,6 +146,70 @@
|
|
|
146
146
|
"ok": {
|
|
147
147
|
"type": "boolean"
|
|
148
148
|
},
|
|
149
|
+
"live": {
|
|
150
|
+
"description": "Present with --live --env (#2549, ws-062): the declared output links resolved against the live graph of the environment. Each member of kind chant ran chant graph --live. Added by chant 0.101.0.",
|
|
151
|
+
"type": "object",
|
|
152
|
+
"required": [
|
|
153
|
+
"env",
|
|
154
|
+
"links"
|
|
155
|
+
],
|
|
156
|
+
"properties": {
|
|
157
|
+
"env": {
|
|
158
|
+
"type": "string"
|
|
159
|
+
},
|
|
160
|
+
"links": {
|
|
161
|
+
"type": "array",
|
|
162
|
+
"items": {
|
|
163
|
+
"type": "object",
|
|
164
|
+
"required": [
|
|
165
|
+
"consumer",
|
|
166
|
+
"producer",
|
|
167
|
+
"output",
|
|
168
|
+
"resolves",
|
|
169
|
+
"status",
|
|
170
|
+
"reason"
|
|
171
|
+
],
|
|
172
|
+
"properties": {
|
|
173
|
+
"consumer": {
|
|
174
|
+
"type": "string"
|
|
175
|
+
},
|
|
176
|
+
"producer": {
|
|
177
|
+
"type": "string"
|
|
178
|
+
},
|
|
179
|
+
"output": {
|
|
180
|
+
"type": "string"
|
|
181
|
+
},
|
|
182
|
+
"kind": {
|
|
183
|
+
"type": "string"
|
|
184
|
+
},
|
|
185
|
+
"origin": {
|
|
186
|
+
"const": "declared"
|
|
187
|
+
},
|
|
188
|
+
"resolves": {
|
|
189
|
+
"const": "live"
|
|
190
|
+
},
|
|
191
|
+
"status": {
|
|
192
|
+
"enum": [
|
|
193
|
+
"resolved",
|
|
194
|
+
"missing",
|
|
195
|
+
"unresolved",
|
|
196
|
+
"invalid"
|
|
197
|
+
]
|
|
198
|
+
},
|
|
199
|
+
"reason": {
|
|
200
|
+
"type": [
|
|
201
|
+
"string",
|
|
202
|
+
"null"
|
|
203
|
+
]
|
|
204
|
+
},
|
|
205
|
+
"pointer": {
|
|
206
|
+
"type": "string"
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
},
|
|
149
213
|
"diagnostics": {
|
|
150
214
|
"description": "Active findings, in the shape `chant lint --format json` prints, sorted by position.",
|
|
151
215
|
"type": "array",
|
|
@@ -16,12 +16,13 @@
|
|
|
16
16
|
* | WSP095 | an inferred join is ambiguous: a parameter matches two or more outputs |
|
|
17
17
|
* | WSP096 | a consumer states the same link twice |
|
|
18
18
|
* | WSP097 | an entry lists `outputs` for a kind that doesn't take them from the entry |
|
|
19
|
+
* | WSP098 | a link states a `protocol` and its kind is not `telemetry` (#2558) |
|
|
19
20
|
*/
|
|
20
21
|
|
|
21
22
|
import type { WorkspaceCheck, WorkspaceCheckContext, WorkspaceDiagnostic } from "../checks";
|
|
22
23
|
import type { Member } from "../declaration";
|
|
23
24
|
import { sourceMemberHandles } from "../member-handles";
|
|
24
|
-
import { DEFAULT_LINK_KIND, LINK_KINDS, linkTargetProblem, resolveLinks, type AmbiguousRow, type LinkRow, type LinkTableRow } from "../links";
|
|
25
|
+
import { DEFAULT_LINK_KIND, LINK_KINDS, TELEMETRY_LINK_KIND, linkTargetProblem, resolveLinks, type AmbiguousRow, type LinkRow, type LinkTableRow } from "../links";
|
|
25
26
|
|
|
26
27
|
const tables = new WeakMap<WorkspaceCheckContext, LinkTableRow[]>();
|
|
27
28
|
|
|
@@ -62,7 +63,7 @@ export const LINK_CHECKS: readonly WorkspaceCheck[] = [
|
|
|
62
63
|
{
|
|
63
64
|
id: "WSP092",
|
|
64
65
|
name: "link-kind-unknown",
|
|
65
|
-
description: `Every member link's kind is one chant knows. Unknown link kinds fail closed; ${DEFAULT_LINK_KIND} is the default and
|
|
66
|
+
description: `Every member link's kind is one chant knows. Unknown link kinds fail closed; ${DEFAULT_LINK_KIND} is the default, and ${LINK_KINDS.filter((k) => k !== DEFAULT_LINK_KIND).join(", ")} is the other.`,
|
|
66
67
|
severity: "error",
|
|
67
68
|
configurable: false,
|
|
68
69
|
check(ctx) {
|
|
@@ -180,4 +181,24 @@ export const LINK_CHECKS: readonly WorkspaceCheck[] = [
|
|
|
180
181
|
});
|
|
181
182
|
},
|
|
182
183
|
},
|
|
184
|
+
{
|
|
185
|
+
id: "WSP098",
|
|
186
|
+
name: "link-protocol-misplaced",
|
|
187
|
+
description: "A link states a protocol only when it is a telemetry link: protocol is the OTLP protocol the consumer sends to the producer's collector, and no other link kind has one.",
|
|
188
|
+
severity: "error",
|
|
189
|
+
configurable: true,
|
|
190
|
+
check(ctx) {
|
|
191
|
+
return ctx.declaration.members.flatMap((m) =>
|
|
192
|
+
m.links
|
|
193
|
+
.filter((l) => l.protocol !== null && (l.kind ?? DEFAULT_LINK_KIND) !== TELEMETRY_LINK_KIND)
|
|
194
|
+
.map((l) => ({
|
|
195
|
+
checkId: this.id,
|
|
196
|
+
severity: this.severity,
|
|
197
|
+
message: `member ${m.name}'s link to ${l.member} output ${l.output} states protocol ${l.protocol}, and its kind is ${l.kind ?? DEFAULT_LINK_KIND}; protocol belongs to a ${TELEMETRY_LINK_KIND} link`,
|
|
198
|
+
entity: m.name,
|
|
199
|
+
pointer: `${l.pointer}/protocol`,
|
|
200
|
+
})),
|
|
201
|
+
);
|
|
202
|
+
},
|
|
203
|
+
},
|
|
183
204
|
];
|