@cat-factory/contracts 0.243.0 → 0.244.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/errors.d.ts +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +7 -0
- package/dist/errors.js.map +1 -1
- package/dist/merge.d.ts +244 -2
- package/dist/merge.d.ts.map +1 -1
- package/dist/merge.js +101 -1
- package/dist/merge.js.map +1 -1
- package/dist/risk-policy-selection.d.ts +10 -4
- package/dist/risk-policy-selection.d.ts.map +1 -1
- package/dist/risk-policy-selection.js +18 -1
- package/dist/risk-policy-selection.js.map +1 -1
- package/dist/routes/merge.d.ts +228 -0
- package/dist/routes/merge.d.ts.map +1 -1
- package/dist/routes/workspaces.d.ts +76 -0
- package/dist/routes/workspaces.d.ts.map +1 -1
- package/dist/snapshot.d.ts +38 -0
- package/dist/snapshot.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/errors.d.ts
CHANGED
|
@@ -29,7 +29,7 @@ export type ApiErrorCode = (typeof API_ERROR_CODES)[number];
|
|
|
29
29
|
* type for `ConflictError`, and the SPA keys an exhaustive `Record<ConflictReason, …>` of
|
|
30
30
|
* localized titles off it — adding a value without a title trips the frontend typecheck.
|
|
31
31
|
*/
|
|
32
|
-
export declare const CONFLICT_REASONS: readonly ['providers_unconfigured', 'preset_unsatisfiable', 'dependencies_unmet', 'task_limit_reached', 'tester_infra_unsupported', 'binary_storage_unconfigured', 'agent_backend_unconfigured', 'run_not_retryable', 'no_pr_to_merge', 'dry_run_not_mergeable', 'github_not_connected', 'bootstrap_not_retryable', 'bootstrap_reference_missing', 'document_already_linked', 'provision_type_unhandled', 'visual_pipeline_no_frontend', 'model_policy_blocked', 'model_policy_unsupported', 'deployer_required_before_tester', 'deployer_service_provisioning_incomplete', 'deployer_connection_test_failed', 'env_test_not_a_frame', 'env_test_infraless', 'env_test_not_provisionable', 'env_test_no_vcs', 'env_test_connection_failed', 'review_debt_warn', 'review_debt_blocked', 'prompt_revision_conflict', 'pipeline_schedule_attached', 'pipeline_schedule_requires_recurring', 'pipeline_schedule_intake_unconfigured', 'foundational_service_exists', 'binary_output_service_invalid', 'binary_output_generator_invalid', 'foundational_service_not_inherited', 'ticket_already_linked', 'input_gate_not_parked', 'input_gate_parked'];
|
|
32
|
+
export declare const CONFLICT_REASONS: readonly ['providers_unconfigured', 'preset_unsatisfiable', 'dependencies_unmet', 'task_limit_reached', 'tester_infra_unsupported', 'binary_storage_unconfigured', 'agent_backend_unconfigured', 'run_not_retryable', 'no_pr_to_merge', 'dry_run_not_mergeable', 'submission_not_allowed', 'github_not_connected', 'bootstrap_not_retryable', 'bootstrap_reference_missing', 'document_already_linked', 'provision_type_unhandled', 'visual_pipeline_no_frontend', 'model_policy_blocked', 'model_policy_unsupported', 'deployer_required_before_tester', 'deployer_service_provisioning_incomplete', 'deployer_connection_test_failed', 'env_test_not_a_frame', 'env_test_infraless', 'env_test_not_provisionable', 'env_test_no_vcs', 'env_test_connection_failed', 'review_debt_warn', 'review_debt_blocked', 'prompt_revision_conflict', 'pipeline_schedule_attached', 'pipeline_schedule_requires_recurring', 'pipeline_schedule_intake_unconfigured', 'foundational_service_exists', 'binary_output_service_invalid', 'binary_output_generator_invalid', 'foundational_service_not_inherited', 'ticket_already_linked', 'input_gate_not_parked', 'input_gate_parked'];
|
|
33
33
|
export type ConflictReason = (typeof CONFLICT_REASONS)[number];
|
|
34
34
|
/**
|
|
35
35
|
* Machine-readable reasons behind a 503 (`error.details.reason` on an `UnavailableError`), for
|
package/dist/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB,YAC7B,WAAW,EACX,YAAY,EACZ,UAAU,EACV,qBAAqB,EACrB,WAAW,EACX,aAAa,EACb,cAAc,EACd,cAAc,CACN,CAAA;AAEV,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAA;AAEjE;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,iJAA+C,CAAA;AAE3E,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAA;AAE3D;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB,YAC3B,wBAAwB,EAKxB,sBAAsB,EACtB,oBAAoB,EACpB,oBAAoB,EACpB,0BAA0B,EAC1B,6BAA6B,EAC7B,4BAA4B,EAC5B,mBAAmB,EACnB,gBAAgB,EAKhB,uBAAuB,
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB,YAC7B,WAAW,EACX,YAAY,EACZ,UAAU,EACV,qBAAqB,EACrB,WAAW,EACX,aAAa,EACb,cAAc,EACd,cAAc,CACN,CAAA;AAEV,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAA;AAEjE;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,iJAA+C,CAAA;AAE3E,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAA;AAE3D;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB,YAC3B,wBAAwB,EAKxB,sBAAsB,EACtB,oBAAoB,EACpB,oBAAoB,EACpB,0BAA0B,EAC1B,6BAA6B,EAC7B,4BAA4B,EAC5B,mBAAmB,EACnB,gBAAgB,EAKhB,uBAAuB,EAOvB,wBAAwB,EACxB,sBAAsB,EACtB,yBAAyB,EACzB,6BAA6B,EAK7B,yBAAyB,EAGzB,0BAA0B,EAG1B,6BAA6B,EAG7B,sBAAsB,EAGtB,0BAA0B,EAI1B,iCAAiC,EAKjC,0CAA0C,EAK1C,iCAAiC,EAGjC,sBAAsB,EAGtB,oBAAoB,EAGpB,4BAA4B,EAG5B,iBAAiB,EAMjB,4BAA4B,EAK5B,kBAAkB,EAIlB,qBAAqB,EAKrB,0BAA0B,EAO1B,4BAA4B,EAG5B,sCAAsC,EAItC,uCAAuC,EAKvC,6BAA6B,EAS7B,+BAA+B,EAY/B,iCAAiC,EAOjC,oCAAoC,EAOpC,uBAAuB,EAKvB,uBAAuB,EAUvB,mBAAmB,CACX,CAAA;AAEV,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAA;AAE9D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,mBAAmB,YAC9B,+BAA+B,EAC/B,mCAAmC,CAC3B,CAAA;AAEV,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAA;AAEpE;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,qBAAqB,YAAI,qBAAqB,EAAE,yBAAyB,CAAU,CAAA;AAEhG,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAA"}
|
package/dist/errors.js
CHANGED
|
@@ -56,6 +56,13 @@ export const CONFLICT_REASONS = [
|
|
|
56
56
|
// authority to land it, and the remedy is to re-run the task live rather than to look for a
|
|
57
57
|
// PR that does not exist.
|
|
58
58
|
'dry_run_not_mergeable',
|
|
59
|
+
// The run's initiator holds a role whose merge preset allowlists which change classes it may
|
|
60
|
+
// land, and this pull request's class is outside that list. Kept apart from
|
|
61
|
+
// `dry_run_not_mergeable` because the remedies are opposite: re-running live changes nothing
|
|
62
|
+
// here (the same role would produce the same refusal), and what does resolve it is a teammate
|
|
63
|
+
// whose role may land this class, or an operator widening the allowlist. `details.changeClass`
|
|
64
|
+
// names the class that was refused.
|
|
65
|
+
'submission_not_allowed',
|
|
59
66
|
'github_not_connected',
|
|
60
67
|
'bootstrap_not_retryable',
|
|
61
68
|
'bootstrap_reference_missing',
|
package/dist/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
|
package/dist/merge.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as v from 'valibot';
|
|
2
|
-
import { type ChangeClass } from './mergeTrackRecord.js';
|
|
2
|
+
import { type ChangeClass, type RuleableChangeClass } from './mergeTrackRecord.js';
|
|
3
3
|
import { type WorkspaceRole } from './workspace-members.js';
|
|
4
4
|
/**
|
|
5
5
|
* A `merger` agent's structured assessment of a pull request. Each axis is scored
|
|
@@ -620,6 +620,73 @@ export type ClassRulesByRole = v.InferOutput<typeof classRulesByRoleSchema>;
|
|
|
620
620
|
*/
|
|
621
621
|
export declare const dryRunRolesSchema: v.ArraySchema<v.PicklistSchema<readonly ["admin", "member", "viewer"], undefined>, undefined>;
|
|
622
622
|
export type DryRunRoles = v.InferOutput<typeof dryRunRolesSchema>;
|
|
623
|
+
/**
|
|
624
|
+
* The change classes a preset will LAND at all for a given role: a partial map from
|
|
625
|
+
* {@link workspaceRoleSchema} to the list of classes that role's runs may submit.
|
|
626
|
+
*
|
|
627
|
+
* This is the third role-scoped setting, and it answers a question neither of its siblings can.
|
|
628
|
+
* `classRulesByRole` with `never` routes a class to a human, but the human it routes to may be
|
|
629
|
+
* the initiator themselves: the review card carries a merge button and the RBAC write floor is
|
|
630
|
+
* `member`, so a member's run can raise its own card and land on the next tap. `dryRunRoles`
|
|
631
|
+
* closes that by refusing both exits, but only for EVERY class at once. Between them sits the
|
|
632
|
+
* thing a workspace most often wants to say: a product manager may land copy and dependency
|
|
633
|
+
* bumps on this service, and may not land source, however good the scores look.
|
|
634
|
+
*
|
|
635
|
+
* Three readings define it, and each is deliberate:
|
|
636
|
+
*
|
|
637
|
+
* - **An ALLOWLIST, never a denylist.** A class nobody thought about is OUTSIDE the list, so a
|
|
638
|
+
* class added to the vocabulary in a later release is refused for a scoped role rather than
|
|
639
|
+
* silently landed by it. Same safety property as {@link narrowMergeClassRule}: authoring one
|
|
640
|
+
* can subtract capability, never add it.
|
|
641
|
+
* - **Absent means UNRESTRICTED, not empty.** Silence is not an empty allowlist, exactly as an
|
|
642
|
+
* absent class is not a `thresholds` rule in {@link classRulesByRoleSchema}. Only a role
|
|
643
|
+
* somebody wrote an entry for is scoped, so `{}` is the identity the wire contract says it is
|
|
644
|
+
* (and an EMPTY array is a real, different policy: that role lands nothing).
|
|
645
|
+
* - **`unknown` is INERT.** An unreadable diff must not hold back a run that would otherwise
|
|
646
|
+
* have landed, the same reading `resolveMergeClassRule` takes: a VCS outage cannot change
|
|
647
|
+
* policy. This is the OPPOSITE direction from the first reading, and deliberately so: a class
|
|
648
|
+
* we have never heard of is a policy gap, while a class we could not READ is an outage, and
|
|
649
|
+
* only one of those is evidence about the change.
|
|
650
|
+
*/
|
|
651
|
+
export declare const submissionClassesByRoleSchema: Omit<v.StrictObjectSchema<{
|
|
652
|
+
admin: v.ArraySchema<v.PicklistSchema<readonly ["docs", "test", "dependency", "config", "source", "schema"], undefined>, undefined>;
|
|
653
|
+
member: v.ArraySchema<v.PicklistSchema<readonly ["docs", "test", "dependency", "config", "source", "schema"], undefined>, undefined>;
|
|
654
|
+
viewer: v.ArraySchema<v.PicklistSchema<readonly ["docs", "test", "dependency", "config", "source", "schema"], undefined>, undefined>;
|
|
655
|
+
}, undefined>, "entries" | "~run" | "~standard" | "~types"> & {
|
|
656
|
+
readonly entries: {
|
|
657
|
+
admin: v.OptionalSchema<v.ArraySchema<v.PicklistSchema<readonly ["docs", "test", "dependency", "config", "source", "schema"], undefined>, undefined>, undefined>;
|
|
658
|
+
member: v.OptionalSchema<v.ArraySchema<v.PicklistSchema<readonly ["docs", "test", "dependency", "config", "source", "schema"], undefined>, undefined>, undefined>;
|
|
659
|
+
viewer: v.OptionalSchema<v.ArraySchema<v.PicklistSchema<readonly ["docs", "test", "dependency", "config", "source", "schema"], undefined>, undefined>, undefined>;
|
|
660
|
+
};
|
|
661
|
+
readonly "~standard": v.StandardProps<{
|
|
662
|
+
admin?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
663
|
+
member?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
664
|
+
viewer?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
665
|
+
}, {
|
|
666
|
+
admin?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
667
|
+
member?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
668
|
+
viewer?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
669
|
+
}>;
|
|
670
|
+
readonly "~run": (dataset: v.UnknownDataset, config: v.Config<v.BaseIssue<unknown>>) => v.OutputDataset<{
|
|
671
|
+
admin?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
672
|
+
member?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
673
|
+
viewer?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
674
|
+
}, v.ArrayIssue | v.PicklistIssue | v.StrictObjectIssue>;
|
|
675
|
+
readonly "~types"?: {
|
|
676
|
+
readonly input: {
|
|
677
|
+
admin?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
678
|
+
member?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
679
|
+
viewer?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
680
|
+
};
|
|
681
|
+
readonly output: {
|
|
682
|
+
admin?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
683
|
+
member?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
684
|
+
viewer?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
685
|
+
};
|
|
686
|
+
readonly issue: v.ArrayIssue | v.PicklistIssue | v.StrictObjectIssue;
|
|
687
|
+
} | undefined;
|
|
688
|
+
};
|
|
689
|
+
export type SubmissionClassesByRole = v.InferOutput<typeof submissionClassesByRoleSchema>;
|
|
623
690
|
/**
|
|
624
691
|
* Compose a base rule with a ROLE's rule, taking the STRICTER of the two.
|
|
625
692
|
*
|
|
@@ -702,6 +769,30 @@ export declare function resolveRoleScopedMergeClassRule(input: {
|
|
|
702
769
|
* re-decide what an absent role means.
|
|
703
770
|
*/
|
|
704
771
|
export declare function dryRunForcedForRole(dryRunRoles: readonly WorkspaceRole[] | null | undefined, role: WorkspaceRole | null | undefined): boolean;
|
|
772
|
+
/**
|
|
773
|
+
* The allowlist that governs one run, or `undefined` when nothing does.
|
|
774
|
+
*
|
|
775
|
+
* Kept apart from {@link submissionAllowedForRole} because the two questions differ: "is this run
|
|
776
|
+
* scoped at all" decides whether the merge path owes a classification (one VCS call), and only
|
|
777
|
+
* then does "may it land THIS class" have anything to compare. Reading the boolean first and the
|
|
778
|
+
* verdict second is what keeps an unscoped preset paying for neither.
|
|
779
|
+
*/
|
|
780
|
+
export declare function submissionAllowlistForRole(byRole: SubmissionClassesByRole | null | undefined, role: WorkspaceRole | null | undefined): readonly RuleableChangeClass[] | undefined;
|
|
781
|
+
/**
|
|
782
|
+
* Whether a preset lets a run started by `role` LAND a change of `changeClass`.
|
|
783
|
+
*
|
|
784
|
+
* Every absence answers `true`, and each for its own reason: no pinned role matches no entry, a
|
|
785
|
+
* role with no authored entry is unrestricted, and an `unknown` class is an unreadable diff
|
|
786
|
+
* rather than evidence about the change. Only a role that HAS an allowlist, on a diff that
|
|
787
|
+
* classified, can be refused, and then the list is exhaustive, so a class outside it is refused
|
|
788
|
+
* whether the operator excluded it on purpose or the vocabulary gained it after they wrote the
|
|
789
|
+
* policy.
|
|
790
|
+
*
|
|
791
|
+
* It lives in contracts, beside the map it reads, because the SPA has to state the same verdict:
|
|
792
|
+
* a merge control that offered to land a change the engine will refuse would be advertising a
|
|
793
|
+
* capability the person does not have.
|
|
794
|
+
*/
|
|
795
|
+
export declare function submissionAllowedForRole(byRole: SubmissionClassesByRole | null | undefined, role: WorkspaceRole | null | undefined, changeClass: ChangeClass): boolean;
|
|
705
796
|
export declare const mergeAssessmentSchema: v.ObjectSchema<{
|
|
706
797
|
/** How intricate the change is (size, coupling, subtlety). */
|
|
707
798
|
readonly complexity: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.MinValueAction<number, 0, undefined>, v.MaxValueAction<number, 1, undefined>]>;
|
|
@@ -1373,6 +1464,51 @@ export declare const riskPolicySchema: v.ObjectSchema<{
|
|
|
1373
1464
|
* and the PR opens, but nothing merges. Empty on the built-ins.
|
|
1374
1465
|
*/
|
|
1375
1466
|
readonly dryRunRoles: v.ArraySchema<v.PicklistSchema<readonly ["admin", "member", "viewer"], undefined>, undefined>;
|
|
1467
|
+
/**
|
|
1468
|
+
* Per-ROLE allowlist of the change classes this preset will land at all
|
|
1469
|
+
* ({@link submissionClassesByRoleSchema}). Orthogonal to `classRulesByRole`, and both apply: a
|
|
1470
|
+
* class may be `always` under the role's class rules and still outside its allowlist, and the
|
|
1471
|
+
* allowlist wins, because it bars landing rather than deciding how much review landing takes.
|
|
1472
|
+
* A role with no entry is unrestricted, so `{}` is the identity. Empty on the built-ins.
|
|
1473
|
+
*/
|
|
1474
|
+
readonly submissionClassesByRole: Omit<v.StrictObjectSchema<{
|
|
1475
|
+
admin: v.ArraySchema<v.PicklistSchema<readonly ["docs", "test", "dependency", "config", "source", "schema"], undefined>, undefined>;
|
|
1476
|
+
member: v.ArraySchema<v.PicklistSchema<readonly ["docs", "test", "dependency", "config", "source", "schema"], undefined>, undefined>;
|
|
1477
|
+
viewer: v.ArraySchema<v.PicklistSchema<readonly ["docs", "test", "dependency", "config", "source", "schema"], undefined>, undefined>;
|
|
1478
|
+
}, undefined>, "entries" | "~run" | "~standard" | "~types"> & {
|
|
1479
|
+
readonly entries: {
|
|
1480
|
+
admin: v.OptionalSchema<v.ArraySchema<v.PicklistSchema<readonly ["docs", "test", "dependency", "config", "source", "schema"], undefined>, undefined>, undefined>;
|
|
1481
|
+
member: v.OptionalSchema<v.ArraySchema<v.PicklistSchema<readonly ["docs", "test", "dependency", "config", "source", "schema"], undefined>, undefined>, undefined>;
|
|
1482
|
+
viewer: v.OptionalSchema<v.ArraySchema<v.PicklistSchema<readonly ["docs", "test", "dependency", "config", "source", "schema"], undefined>, undefined>, undefined>;
|
|
1483
|
+
};
|
|
1484
|
+
readonly "~standard": v.StandardProps<{
|
|
1485
|
+
admin?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
1486
|
+
member?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
1487
|
+
viewer?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
1488
|
+
}, {
|
|
1489
|
+
admin?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
1490
|
+
member?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
1491
|
+
viewer?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
1492
|
+
}>;
|
|
1493
|
+
readonly "~run": (dataset: v.UnknownDataset, config: v.Config<v.BaseIssue<unknown>>) => v.OutputDataset<{
|
|
1494
|
+
admin?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
1495
|
+
member?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
1496
|
+
viewer?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
1497
|
+
}, v.ArrayIssue | v.PicklistIssue | v.StrictObjectIssue>;
|
|
1498
|
+
readonly "~types"?: {
|
|
1499
|
+
readonly input: {
|
|
1500
|
+
admin?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
1501
|
+
member?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
1502
|
+
viewer?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
1503
|
+
};
|
|
1504
|
+
readonly output: {
|
|
1505
|
+
admin?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
1506
|
+
member?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
1507
|
+
viewer?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
1508
|
+
};
|
|
1509
|
+
readonly issue: v.ArrayIssue | v.PicklistIssue | v.StrictObjectIssue;
|
|
1510
|
+
} | undefined;
|
|
1511
|
+
};
|
|
1376
1512
|
/** The workspace's fallback preset, used by tasks that pick none. Exactly one is true. */
|
|
1377
1513
|
readonly isDefault: v.BooleanSchema<undefined>;
|
|
1378
1514
|
/**
|
|
@@ -1964,6 +2100,45 @@ export declare const createRiskPolicySchema: v.ObjectSchema<{
|
|
|
1964
2100
|
}, {}>;
|
|
1965
2101
|
/** Roles whose runs are forced into dry-run mode; absent ⇒ nobody is sandboxed. */
|
|
1966
2102
|
readonly dryRunRoles: v.OptionalSchema<v.ArraySchema<v.PicklistSchema<readonly ["admin", "member", "viewer"], undefined>, undefined>, readonly []>;
|
|
2103
|
+
/** Per-role allowlist of landable change classes; absent ⇒ every role is unrestricted. */
|
|
2104
|
+
readonly submissionClassesByRole: v.OptionalSchema<Omit<v.StrictObjectSchema<{
|
|
2105
|
+
admin: v.ArraySchema<v.PicklistSchema<readonly ["docs", "test", "dependency", "config", "source", "schema"], undefined>, undefined>;
|
|
2106
|
+
member: v.ArraySchema<v.PicklistSchema<readonly ["docs", "test", "dependency", "config", "source", "schema"], undefined>, undefined>;
|
|
2107
|
+
viewer: v.ArraySchema<v.PicklistSchema<readonly ["docs", "test", "dependency", "config", "source", "schema"], undefined>, undefined>;
|
|
2108
|
+
}, undefined>, "entries" | "~run" | "~standard" | "~types"> & {
|
|
2109
|
+
readonly entries: {
|
|
2110
|
+
admin: v.OptionalSchema<v.ArraySchema<v.PicklistSchema<readonly ["docs", "test", "dependency", "config", "source", "schema"], undefined>, undefined>, undefined>;
|
|
2111
|
+
member: v.OptionalSchema<v.ArraySchema<v.PicklistSchema<readonly ["docs", "test", "dependency", "config", "source", "schema"], undefined>, undefined>, undefined>;
|
|
2112
|
+
viewer: v.OptionalSchema<v.ArraySchema<v.PicklistSchema<readonly ["docs", "test", "dependency", "config", "source", "schema"], undefined>, undefined>, undefined>;
|
|
2113
|
+
};
|
|
2114
|
+
readonly "~standard": v.StandardProps<{
|
|
2115
|
+
admin?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
2116
|
+
member?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
2117
|
+
viewer?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
2118
|
+
}, {
|
|
2119
|
+
admin?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
2120
|
+
member?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
2121
|
+
viewer?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
2122
|
+
}>;
|
|
2123
|
+
readonly "~run": (dataset: v.UnknownDataset, config: v.Config<v.BaseIssue<unknown>>) => v.OutputDataset<{
|
|
2124
|
+
admin?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
2125
|
+
member?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
2126
|
+
viewer?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
2127
|
+
}, v.ArrayIssue | v.PicklistIssue | v.StrictObjectIssue>;
|
|
2128
|
+
readonly "~types"?: {
|
|
2129
|
+
readonly input: {
|
|
2130
|
+
admin?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
2131
|
+
member?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
2132
|
+
viewer?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
2133
|
+
};
|
|
2134
|
+
readonly output: {
|
|
2135
|
+
admin?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
2136
|
+
member?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
2137
|
+
viewer?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
2138
|
+
};
|
|
2139
|
+
readonly issue: v.ArrayIssue | v.PicklistIssue | v.StrictObjectIssue;
|
|
2140
|
+
} | undefined;
|
|
2141
|
+
}, {}>;
|
|
1967
2142
|
/** Make this the workspace default (demotes the previous default). */
|
|
1968
2143
|
readonly isDefault: v.OptionalSchema<v.BooleanSchema<undefined>, false>;
|
|
1969
2144
|
}, undefined>;
|
|
@@ -2545,6 +2720,45 @@ export declare const updateRiskPolicySchema: v.ObjectSchema<{
|
|
|
2545
2720
|
}, undefined>;
|
|
2546
2721
|
/** Replaces the whole list, so un-sandboxing a role is a plain omission. */
|
|
2547
2722
|
readonly dryRunRoles: v.OptionalSchema<v.ArraySchema<v.PicklistSchema<readonly ["admin", "member", "viewer"], undefined>, undefined>, undefined>;
|
|
2723
|
+
/** Replaces the whole map, so un-scoping a role is a plain omission (never an empty array). */
|
|
2724
|
+
readonly submissionClassesByRole: v.OptionalSchema<Omit<v.StrictObjectSchema<{
|
|
2725
|
+
admin: v.ArraySchema<v.PicklistSchema<readonly ["docs", "test", "dependency", "config", "source", "schema"], undefined>, undefined>;
|
|
2726
|
+
member: v.ArraySchema<v.PicklistSchema<readonly ["docs", "test", "dependency", "config", "source", "schema"], undefined>, undefined>;
|
|
2727
|
+
viewer: v.ArraySchema<v.PicklistSchema<readonly ["docs", "test", "dependency", "config", "source", "schema"], undefined>, undefined>;
|
|
2728
|
+
}, undefined>, "entries" | "~run" | "~standard" | "~types"> & {
|
|
2729
|
+
readonly entries: {
|
|
2730
|
+
admin: v.OptionalSchema<v.ArraySchema<v.PicklistSchema<readonly ["docs", "test", "dependency", "config", "source", "schema"], undefined>, undefined>, undefined>;
|
|
2731
|
+
member: v.OptionalSchema<v.ArraySchema<v.PicklistSchema<readonly ["docs", "test", "dependency", "config", "source", "schema"], undefined>, undefined>, undefined>;
|
|
2732
|
+
viewer: v.OptionalSchema<v.ArraySchema<v.PicklistSchema<readonly ["docs", "test", "dependency", "config", "source", "schema"], undefined>, undefined>, undefined>;
|
|
2733
|
+
};
|
|
2734
|
+
readonly "~standard": v.StandardProps<{
|
|
2735
|
+
admin?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
2736
|
+
member?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
2737
|
+
viewer?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
2738
|
+
}, {
|
|
2739
|
+
admin?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
2740
|
+
member?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
2741
|
+
viewer?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
2742
|
+
}>;
|
|
2743
|
+
readonly "~run": (dataset: v.UnknownDataset, config: v.Config<v.BaseIssue<unknown>>) => v.OutputDataset<{
|
|
2744
|
+
admin?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
2745
|
+
member?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
2746
|
+
viewer?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
2747
|
+
}, v.ArrayIssue | v.PicklistIssue | v.StrictObjectIssue>;
|
|
2748
|
+
readonly "~types"?: {
|
|
2749
|
+
readonly input: {
|
|
2750
|
+
admin?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
2751
|
+
member?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
2752
|
+
viewer?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
2753
|
+
};
|
|
2754
|
+
readonly output: {
|
|
2755
|
+
admin?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
2756
|
+
member?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
2757
|
+
viewer?: ("config" | "dependency" | "docs" | "schema" | "source" | "test")[] | undefined;
|
|
2758
|
+
};
|
|
2759
|
+
readonly issue: v.ArrayIssue | v.PicklistIssue | v.StrictObjectIssue;
|
|
2760
|
+
} | undefined;
|
|
2761
|
+
}, undefined>;
|
|
2548
2762
|
readonly isDefault: v.OptionalSchema<v.BooleanSchema<undefined>, undefined>;
|
|
2549
2763
|
}, undefined>;
|
|
2550
2764
|
export type UpdateRiskPolicyInput = v.InferOutput<typeof updateRiskPolicySchema>;
|
|
@@ -2583,6 +2797,17 @@ export declare const mergeDecisionThresholdsSchema: v.ObjectSchema<{
|
|
|
2583
2797
|
* someone else".
|
|
2584
2798
|
*/
|
|
2585
2799
|
readonly roleRule: v.OptionalSchema<v.PicklistSchema<readonly ["thresholds", "always", "never"], undefined>, undefined>;
|
|
2800
|
+
/**
|
|
2801
|
+
* The change classes the initiator's role may land at all, recorded whenever that role carried
|
|
2802
|
+
* a submission allowlist, not only when the allowlist is what held the PR back.
|
|
2803
|
+
*
|
|
2804
|
+
* Present-means-scoped is the useful reading here (the opposite of `roleRule` above, which is
|
|
2805
|
+
* recorded only when it changed the outcome): an allowlist that PERMITTED this class is the
|
|
2806
|
+
* fact that explains why an otherwise identical PR on another class will not land, and a
|
|
2807
|
+
* banner that mentioned the scope only on the refusal would make the permission look like an
|
|
2808
|
+
* absence of policy.
|
|
2809
|
+
*/
|
|
2810
|
+
readonly submissionClasses: v.OptionalSchema<v.ArraySchema<v.PicklistSchema<readonly ["docs", "test", "dependency", "config", "source", "schema"], undefined>, undefined>, undefined>;
|
|
2586
2811
|
}, undefined>;
|
|
2587
2812
|
export type MergeDecisionThresholds = v.InferOutput<typeof mergeDecisionThresholdsSchema>;
|
|
2588
2813
|
export declare const mergeDecisionSchema: v.ObjectSchema<{
|
|
@@ -2611,12 +2836,18 @@ export declare const mergeDecisionSchema: v.ObjectSchema<{
|
|
|
2611
2836
|
* `class_requires_review` because the two need opposite fixes: one is a policy about the
|
|
2612
2837
|
* KIND of change (edit the class rule), the other about WHO started it (a teammate on a
|
|
2613
2838
|
* higher tier can merge this PR as it stands).
|
|
2839
|
+
* - `submission_not_allowed`: review; the initiator's role carries a submission allowlist and
|
|
2840
|
+
* this run's change class is outside it, so the platform will not land the work whatever the
|
|
2841
|
+
* scores or the class rules say. Kept apart from `role_requires_review` because the remedies
|
|
2842
|
+
* differ in kind: that one is satisfied by ANY reviewer merging the PR through this
|
|
2843
|
+
* platform, while this one refuses the platform merge path outright (the PR is still a real
|
|
2844
|
+
* PR, and someone with write access on the host can merge it there).
|
|
2614
2845
|
* - `dry_run`: review; the run was a DRY RUN, so no outcome of the assessment could have
|
|
2615
2846
|
* merged it. The master switch above every other reason, including `auto_merge_disabled`:
|
|
2616
2847
|
* a preset that would otherwise auto-merge must not report a dry run's PR as "held back by
|
|
2617
2848
|
* the scores", which would send someone editing thresholds that were never consulted.
|
|
2618
2849
|
*/
|
|
2619
|
-
readonly reason: v.PicklistSchema<["within_thresholds", "exceeded_thresholds", "auto_merge_disabled", "no_rationale", "no_assessment", "merge_failed", "merge_partial", "class_auto_merge", "class_requires_review", "role_requires_review", "dry_run"], undefined>;
|
|
2850
|
+
readonly reason: v.PicklistSchema<["within_thresholds", "exceeded_thresholds", "auto_merge_disabled", "no_rationale", "no_assessment", "merge_failed", "merge_partial", "class_auto_merge", "class_requires_review", "role_requires_review", "submission_not_allowed", "dry_run"], undefined>;
|
|
2620
2851
|
/** The merger's assessment (absent only when it produced no parseable one). */
|
|
2621
2852
|
readonly assessment: v.OptionalSchema<v.ObjectSchema<{
|
|
2622
2853
|
/** How intricate the change is (size, coupling, subtlety). */
|
|
@@ -2657,6 +2888,17 @@ export declare const mergeDecisionSchema: v.ObjectSchema<{
|
|
|
2657
2888
|
* someone else".
|
|
2658
2889
|
*/
|
|
2659
2890
|
readonly roleRule: v.OptionalSchema<v.PicklistSchema<readonly ["thresholds", "always", "never"], undefined>, undefined>;
|
|
2891
|
+
/**
|
|
2892
|
+
* The change classes the initiator's role may land at all, recorded whenever that role carried
|
|
2893
|
+
* a submission allowlist, not only when the allowlist is what held the PR back.
|
|
2894
|
+
*
|
|
2895
|
+
* Present-means-scoped is the useful reading here (the opposite of `roleRule` above, which is
|
|
2896
|
+
* recorded only when it changed the outcome): an allowlist that PERMITTED this class is the
|
|
2897
|
+
* fact that explains why an otherwise identical PR on another class will not land, and a
|
|
2898
|
+
* banner that mentioned the scope only on the refusal would make the permission look like an
|
|
2899
|
+
* absence of policy.
|
|
2900
|
+
*/
|
|
2901
|
+
readonly submissionClasses: v.OptionalSchema<v.ArraySchema<v.PicklistSchema<readonly ["docs", "test", "dependency", "config", "source", "schema"], undefined>, undefined>, undefined>;
|
|
2660
2902
|
}, undefined>;
|
|
2661
2903
|
/** The axes that exceeded their ceiling (empty unless `reason` is `exceeded_thresholds`). */
|
|
2662
2904
|
readonly exceededAxes: v.ArraySchema<v.PicklistSchema<["complexity", "risk", "impact"], undefined>, undefined>;
|
package/dist/merge.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAE5B,OAAO,
|
|
1
|
+
{"version":3,"file":"merge.d.ts","sourceRoot":"","sources":["../src/merge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAE5B,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACzB,MAAM,uBAAuB,CAAA;AAE9B,OAAO,EAAwC,KAAK,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAiBjG;;;;GAIG;AACH;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,gEAAgD,CAAA;AAC1F,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,6BAA6B,CAAC,CAAA;AAEzF,+EAA+E;AAC/E,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAAC,uBAAuB,EAAE,MAAM,CAK5E,CAAA;AAUD;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,YAAI,YAAY,EAAE,QAAQ,EAAE,OAAO,CAAU,CAAA;AAE3E,eAAO,MAAM,oBAAoB,yEAAgC,CAAA;AACjE,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,oBAAoB,CAAC,CAAA;AAEvE;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAQjC,CAAA;AACD,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,qBAAqB,CAAC,CAAA;AAEzE;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAQlC,CAAA;AACD,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,sBAAsB,CAAC,CAAA;AAE3E;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,iBAAiB,+FAA+B,CAAA;AAC7D,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,iBAAiB,CAAC,CAAA;AAKjE;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAWzC,CAAA;AACD,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,6BAA6B,CAAC,CAAA;AAiBzF;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,cAAc,GAAG,cAAc,CAE/F;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,cAAc,GAAG,OAAO,CAEzF;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,eAAe,GAAG,IAAI,GAAG,SAAS,EACzC,WAAW,EAAE,WAAW,GACvB,cAAc,CAGhB;AAED,sFAAsF;AACtF,MAAM,WAAW,wBAAwB;IACvC,qEAAqE;IACrE,IAAI,EAAE,cAAc,CAAA;IACpB,4EAA4E;IAC5E,SAAS,EAAE,cAAc,CAAA;IACzB;;;;OAIG;IACH,cAAc,EAAE,OAAO,CAAA;CACxB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,+BAA+B,CAAC,KAAK,EAAE;IACrD,KAAK,EAAE,eAAe,GAAG,IAAI,GAAG,SAAS,CAAA;IACzC,MAAM,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,CAAA;IAC3C,IAAI,EAAE,aAAa,GAAG,IAAI,GAAG,SAAS,CAAA;IACtC,WAAW,EAAE,WAAW,CAAA;CACzB,GAAG,wBAAwB,CAc3B;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,SAAS,aAAa,EAAE,GAAG,IAAI,GAAG,SAAS,EACxD,IAAI,EAAE,aAAa,GAAG,IAAI,GAAG,SAAS,GACrC,OAAO,CAET;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,uBAAuB,GAAG,IAAI,GAAG,SAAS,EAClD,IAAI,EAAE,aAAa,GAAG,IAAI,GAAG,SAAS,GACrC,SAAS,mBAAmB,EAAE,GAAG,SAAS,CAM5C;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,uBAAuB,GAAG,IAAI,GAAG,SAAS,EAClD,IAAI,EAAE,aAAa,GAAG,IAAI,GAAG,SAAS,EACtC,WAAW,EAAE,WAAW,GACvB,OAAO,CAIT;AAED,eAAO,MAAM,qBAAqB;IAChC,8DAA8D;;IAE9D,yEAAyE;;IAEzE,+DAA+D;;IAE/D,qFAAqF;;aAErF,CAAA;AACF,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,qBAAqB,CAAC,CAAA;AAEzE;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB;;;IAG3B,4EAA4E;;IAE5E,sEAAsE;;IAEtE,wEAAwE;;IAExE,qFAAqF;;IAErF;;;;OAIG;;IAEH;;;;;;;OAOG;;IAEH;;;;;;OAMG;;IAEH;;;OAGG;;IAEH;;;;OAIG;;IAEH;;;;;OAKG;;IAEH;;;;;OAKG;;IAEH;;;;OAIG;;IAEH;;;;;OAKG;;IAEH;;;;;;;;;OASG;;;;;;;;IAEH;;;;OAIG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAEH;;;;OAIG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAEH;;;OAGG;;IAEH;;;;;;OAMG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAEH,0FAA0F;;IAE1F;;;;;OAKG;;;aAGH,CAAA;AACF,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,gBAAgB,CAAC,CAAA;AAa/D,0DAA0D;AAC1D,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;IAcjC,mFAAmF;;IAEnF,+FAA+F;;;;;;;;IAE/F,uFAAuF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAEvF,8FAA8F;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAE9F,mFAAmF;;IAEnF,0FAA0F;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAE1F,sEAAsE;;aAEtE,CAAA;AACF,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,sBAAsB,CAAC,CAAA;AAEhF,sEAAsE;AACtE,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;IAgBjC,yFAAyF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAEzF,4FAA4F;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAE5F,4EAA4E;;IAE5E,+FAA+F;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAG/F,CAAA;AACF,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,sBAAsB,CAAC,CAAA;AAEhF,wFAAwF;AACxF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,CAEpE;AAUD,yDAAyD;AACzD,eAAO,MAAM,eAAe,+DAA+C,CAAA;AAC3E,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,eAAe,CAAC,CAAA;AAE7D,yFAAyF;AACzF,eAAO,MAAM,6BAA6B;IACxC,4DAA4D;;;;;;IAM5D;;;;;OAKG;;IAEH;;;;;;OAMG;;IAEH;;;;;OAKG;;IAEH;;;;;;;;;OASG;;aAEH,CAAA;AACF,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,6BAA6B,CAAC,CAAA;AAEzF,eAAO,MAAM,mBAAmB;IAC9B,gFAAgF;;IAEhF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAiCG;;IAeH,+EAA+E;;QAlU/E,8DAA8D;;QAE9D,yEAAyE;;QAEzE,+DAA+D;;QAE/D,qFAAqF;;;;QA+NrF,4DAA4D;;;;;;QAM5D;;;;;WAKG;;QAEH;;;;;;WAMG;;QAEH;;;;;WAKG;;QAEH;;;;;;;;;WASG;;;IA2DH,6FAA6F;;IAE7F;;;;;OAKG;;aAEH,CAAA;AACF,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,mBAAmB,CAAC,CAAA"}
|
package/dist/merge.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as v from 'valibot';
|
|
2
2
|
import { stepGatingSchema } from './consensus.js';
|
|
3
|
-
import { changeClassSchema, RULEABLE_CHANGE_CLASSES } from './mergeTrackRecord.js';
|
|
3
|
+
import { changeClassSchema, RULEABLE_CHANGE_CLASSES, } from './mergeTrackRecord.js';
|
|
4
4
|
import { DEFAULT_JUDGE_MAX_BOUNCES, DEFAULT_JUDGE_MIN_SCORE } from './judge.js';
|
|
5
5
|
import { WORKSPACE_ROLES, workspaceRoleSchema } from './workspace-members.js';
|
|
6
6
|
// ---------------------------------------------------------------------------
|
|
@@ -101,6 +101,40 @@ v.strictObject(Object.fromEntries(WORKSPACE_ROLES.map((r) => [r, mergeClassRules
|
|
|
101
101
|
* cannot start runs at all (`viewer`, which holds no `runs.execute`) may be listed without effect.
|
|
102
102
|
*/
|
|
103
103
|
export const dryRunRolesSchema = v.array(workspaceRoleSchema);
|
|
104
|
+
/** A picklist over the classes a rule (or an allowlist entry) may be authored for. */
|
|
105
|
+
const ruleableChangeClassSchema = v.picklist(RULEABLE_CHANGE_CLASSES);
|
|
106
|
+
/**
|
|
107
|
+
* The change classes a preset will LAND at all for a given role: a partial map from
|
|
108
|
+
* {@link workspaceRoleSchema} to the list of classes that role's runs may submit.
|
|
109
|
+
*
|
|
110
|
+
* This is the third role-scoped setting, and it answers a question neither of its siblings can.
|
|
111
|
+
* `classRulesByRole` with `never` routes a class to a human, but the human it routes to may be
|
|
112
|
+
* the initiator themselves: the review card carries a merge button and the RBAC write floor is
|
|
113
|
+
* `member`, so a member's run can raise its own card and land on the next tap. `dryRunRoles`
|
|
114
|
+
* closes that by refusing both exits, but only for EVERY class at once. Between them sits the
|
|
115
|
+
* thing a workspace most often wants to say: a product manager may land copy and dependency
|
|
116
|
+
* bumps on this service, and may not land source, however good the scores look.
|
|
117
|
+
*
|
|
118
|
+
* Three readings define it, and each is deliberate:
|
|
119
|
+
*
|
|
120
|
+
* - **An ALLOWLIST, never a denylist.** A class nobody thought about is OUTSIDE the list, so a
|
|
121
|
+
* class added to the vocabulary in a later release is refused for a scoped role rather than
|
|
122
|
+
* silently landed by it. Same safety property as {@link narrowMergeClassRule}: authoring one
|
|
123
|
+
* can subtract capability, never add it.
|
|
124
|
+
* - **Absent means UNRESTRICTED, not empty.** Silence is not an empty allowlist, exactly as an
|
|
125
|
+
* absent class is not a `thresholds` rule in {@link classRulesByRoleSchema}. Only a role
|
|
126
|
+
* somebody wrote an entry for is scoped, so `{}` is the identity the wire contract says it is
|
|
127
|
+
* (and an EMPTY array is a real, different policy: that role lands nothing).
|
|
128
|
+
* - **`unknown` is INERT.** An unreadable diff must not hold back a run that would otherwise
|
|
129
|
+
* have landed, the same reading `resolveMergeClassRule` takes: a VCS outage cannot change
|
|
130
|
+
* policy. This is the OPPOSITE direction from the first reading, and deliberately so: a class
|
|
131
|
+
* we have never heard of is a policy gap, while a class we could not READ is an outage, and
|
|
132
|
+
* only one of those is evidence about the change.
|
|
133
|
+
*/
|
|
134
|
+
export const submissionClassesByRoleSchema = v.partial(
|
|
135
|
+
// STRICT for the same reason both rule maps are: a caller who thinks they authored an
|
|
136
|
+
// allowlist must not be told it worked when the role name was a typo.
|
|
137
|
+
v.strictObject(Object.fromEntries(WORKSPACE_ROLES.map((r) => [r, v.array(ruleableChangeClassSchema)]))));
|
|
104
138
|
/**
|
|
105
139
|
* How much review a rule DEMANDS, higher is stricter. The ordering is the point of the scale:
|
|
106
140
|
* `always` merges with nothing consulted, `thresholds` merges only within the score ceilings, and
|
|
@@ -204,6 +238,42 @@ export function resolveRoleScopedMergeClassRule(input) {
|
|
|
204
238
|
export function dryRunForcedForRole(dryRunRoles, role) {
|
|
205
239
|
return !!role && !!dryRunRoles?.includes(role);
|
|
206
240
|
}
|
|
241
|
+
/**
|
|
242
|
+
* The allowlist that governs one run, or `undefined` when nothing does.
|
|
243
|
+
*
|
|
244
|
+
* Kept apart from {@link submissionAllowedForRole} because the two questions differ: "is this run
|
|
245
|
+
* scoped at all" decides whether the merge path owes a classification (one VCS call), and only
|
|
246
|
+
* then does "may it land THIS class" have anything to compare. Reading the boolean first and the
|
|
247
|
+
* verdict second is what keeps an unscoped preset paying for neither.
|
|
248
|
+
*/
|
|
249
|
+
export function submissionAllowlistForRole(byRole, role) {
|
|
250
|
+
// A run with no pinned role matches no entry, exactly as `dryRunForcedForRole` reads one: a
|
|
251
|
+
// schedule fire / public-API start / auth-disabled dev is not a tier, and treating it as one
|
|
252
|
+
// would scope every unattributed run in a deployment the day somebody first scopes a role.
|
|
253
|
+
if (!role)
|
|
254
|
+
return undefined;
|
|
255
|
+
return byRole?.[role];
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Whether a preset lets a run started by `role` LAND a change of `changeClass`.
|
|
259
|
+
*
|
|
260
|
+
* Every absence answers `true`, and each for its own reason: no pinned role matches no entry, a
|
|
261
|
+
* role with no authored entry is unrestricted, and an `unknown` class is an unreadable diff
|
|
262
|
+
* rather than evidence about the change. Only a role that HAS an allowlist, on a diff that
|
|
263
|
+
* classified, can be refused, and then the list is exhaustive, so a class outside it is refused
|
|
264
|
+
* whether the operator excluded it on purpose or the vocabulary gained it after they wrote the
|
|
265
|
+
* policy.
|
|
266
|
+
*
|
|
267
|
+
* It lives in contracts, beside the map it reads, because the SPA has to state the same verdict:
|
|
268
|
+
* a merge control that offered to land a change the engine will refuse would be advertising a
|
|
269
|
+
* capability the person does not have.
|
|
270
|
+
*/
|
|
271
|
+
export function submissionAllowedForRole(byRole, role, changeClass) {
|
|
272
|
+
if (changeClass === 'unknown')
|
|
273
|
+
return true;
|
|
274
|
+
const allowed = submissionAllowlistForRole(byRole, role);
|
|
275
|
+
return !allowed || allowed.includes(changeClass);
|
|
276
|
+
}
|
|
207
277
|
export const mergeAssessmentSchema = v.object({
|
|
208
278
|
/** How intricate the change is (size, coupling, subtlety). */
|
|
209
279
|
complexity: v.pipe(v.number(), v.minValue(0), v.maxValue(1)),
|
|
@@ -320,6 +390,14 @@ export const riskPolicySchema = v.object({
|
|
|
320
390
|
* and the PR opens, but nothing merges. Empty on the built-ins.
|
|
321
391
|
*/
|
|
322
392
|
dryRunRoles: dryRunRolesSchema,
|
|
393
|
+
/**
|
|
394
|
+
* Per-ROLE allowlist of the change classes this preset will land at all
|
|
395
|
+
* ({@link submissionClassesByRoleSchema}). Orthogonal to `classRulesByRole`, and both apply: a
|
|
396
|
+
* class may be `always` under the role's class rules and still outside its allowlist, and the
|
|
397
|
+
* allowlist wins, because it bars landing rather than deciding how much review landing takes.
|
|
398
|
+
* A role with no entry is unrestricted, so `{}` is the identity. Empty on the built-ins.
|
|
399
|
+
*/
|
|
400
|
+
submissionClassesByRole: submissionClassesByRoleSchema,
|
|
323
401
|
/** The workspace's fallback preset, used by tasks that pick none. Exactly one is true. */
|
|
324
402
|
isDefault: v.boolean(),
|
|
325
403
|
/**
|
|
@@ -365,6 +443,8 @@ export const createRiskPolicySchema = v.object({
|
|
|
365
443
|
classRulesByRole: v.optional(classRulesByRoleSchema, {}),
|
|
366
444
|
/** Roles whose runs are forced into dry-run mode; absent ⇒ nobody is sandboxed. */
|
|
367
445
|
dryRunRoles: v.optional(dryRunRolesSchema, []),
|
|
446
|
+
/** Per-role allowlist of landable change classes; absent ⇒ every role is unrestricted. */
|
|
447
|
+
submissionClassesByRole: v.optional(submissionClassesByRoleSchema, {}),
|
|
368
448
|
/** Make this the workspace default (demotes the previous default). */
|
|
369
449
|
isDefault: v.optional(v.boolean(), false),
|
|
370
450
|
});
|
|
@@ -391,6 +471,8 @@ export const updateRiskPolicySchema = v.object({
|
|
|
391
471
|
classRulesByRole: v.optional(classRulesByRoleSchema),
|
|
392
472
|
/** Replaces the whole list, so un-sandboxing a role is a plain omission. */
|
|
393
473
|
dryRunRoles: v.optional(dryRunRolesSchema),
|
|
474
|
+
/** Replaces the whole map, so un-scoping a role is a plain omission (never an empty array). */
|
|
475
|
+
submissionClassesByRole: v.optional(submissionClassesByRoleSchema),
|
|
394
476
|
isDefault: v.optional(v.boolean()),
|
|
395
477
|
});
|
|
396
478
|
/** Parse-or-throw an assessment payload an agent returned (the engine validates it). */
|
|
@@ -436,6 +518,17 @@ export const mergeDecisionThresholdsSchema = v.object({
|
|
|
436
518
|
* someone else".
|
|
437
519
|
*/
|
|
438
520
|
roleRule: v.optional(mergeClassRuleSchema),
|
|
521
|
+
/**
|
|
522
|
+
* The change classes the initiator's role may land at all, recorded whenever that role carried
|
|
523
|
+
* a submission allowlist, not only when the allowlist is what held the PR back.
|
|
524
|
+
*
|
|
525
|
+
* Present-means-scoped is the useful reading here (the opposite of `roleRule` above, which is
|
|
526
|
+
* recorded only when it changed the outcome): an allowlist that PERMITTED this class is the
|
|
527
|
+
* fact that explains why an otherwise identical PR on another class will not land, and a
|
|
528
|
+
* banner that mentioned the scope only on the refusal would make the permission look like an
|
|
529
|
+
* absence of policy.
|
|
530
|
+
*/
|
|
531
|
+
submissionClasses: v.optional(v.array(ruleableChangeClassSchema)),
|
|
439
532
|
});
|
|
440
533
|
export const mergeDecisionSchema = v.object({
|
|
441
534
|
/** What the engine did: merged the PR for real, or left it open for a human. */
|
|
@@ -463,6 +556,12 @@ export const mergeDecisionSchema = v.object({
|
|
|
463
556
|
* `class_requires_review` because the two need opposite fixes: one is a policy about the
|
|
464
557
|
* KIND of change (edit the class rule), the other about WHO started it (a teammate on a
|
|
465
558
|
* higher tier can merge this PR as it stands).
|
|
559
|
+
* - `submission_not_allowed`: review; the initiator's role carries a submission allowlist and
|
|
560
|
+
* this run's change class is outside it, so the platform will not land the work whatever the
|
|
561
|
+
* scores or the class rules say. Kept apart from `role_requires_review` because the remedies
|
|
562
|
+
* differ in kind: that one is satisfied by ANY reviewer merging the PR through this
|
|
563
|
+
* platform, while this one refuses the platform merge path outright (the PR is still a real
|
|
564
|
+
* PR, and someone with write access on the host can merge it there).
|
|
466
565
|
* - `dry_run`: review; the run was a DRY RUN, so no outcome of the assessment could have
|
|
467
566
|
* merged it. The master switch above every other reason, including `auto_merge_disabled`:
|
|
468
567
|
* a preset that would otherwise auto-merge must not report a dry run's PR as "held back by
|
|
@@ -479,6 +578,7 @@ export const mergeDecisionSchema = v.object({
|
|
|
479
578
|
'class_auto_merge',
|
|
480
579
|
'class_requires_review',
|
|
481
580
|
'role_requires_review',
|
|
581
|
+
'submission_not_allowed',
|
|
482
582
|
'dry_run',
|
|
483
583
|
]),
|
|
484
584
|
/** The merger's assessment (absent only when it produced no parseable one). */
|