@webpieces/rules-config 0.4.468 → 0.4.470

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/src/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/index.ts"],"names":[],"mappings":"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","sourcesContent":["export { ResolvedConfig, ResolvedRuleConfig, RuleOptions } from './types';\nexport { InformAiError } from './inform-ai-error';\nexport { RuleFailError } from './rule-fail-error';\nexport { CliExitError } from './cli-exit-error';\nexport { CliUsage, CliArgsCheck, CliArgs } from './cli-args';\nexport { runMain } from './run-main';\nexport { toError } from './to-error';\nexport { loadAndValidate, LoadedConfig, ConfigLoader } from './load-config';\nexport { findConfigFile, CONFIG_FILENAME, ConfigFile } from './config-file';\nexport { RepoRootFinder, INSTRUCT_AI_DIR } from './repo-root';\nexport { RulesConfigDesign } from './rules-config-design';\nexport { DocumentDesign, isDocumentDesign, DESIGN_METADATA_KEYS } from './di';\nexport { ExcludePaths } from './exclude-hook-paths';\nexport { isPathExcluded } from './exclude-paths';\nexport { defaultRules, defaultRulesDir } from './default-rules';\nexport { loadTemplate, writeTemplateIfMissing, writeTemplate, TemplateWriter } from './load-template';\nexport { validateWebpiecesConfig, validatePrGateSection, validateSectionPlacement, validateCommandsSection, validateExcludePaths, validateMatchRulesSection, allRuleNames } from './validate-config';\nexport {\n MatchRuleConfig,\n MatchRuleViolation,\n findMatchRuleViolations,\n isMatchRuleAllowedPath,\n compileMatchRulePatterns,\n renderMatchRuleMessage,\n DEFAULT_MATCH_RULES,\n} from './match-rules-config';\nexport type { ConfigSection } from './sections';\nexport { HOOK_GUARD_NAMES, isHookGuard, sectionForRule } from './sections';\nexport { FieldDef } from './field-def';\nexport type { SchemaShape } from './field-def';\nexport { shouldSkipRule, getCurrentBranch } from './skip-rule';\nexport type { SkipRuleResult } from './skip-rule';\nexport {\n detectBase,\n resolveBase,\n getChangedFiles,\n getFileDiff,\n getChangedLineNumbers,\n findNewMethodSignaturesInDiff,\n hasChangesInRange,\n isNewOrModified,\n DiffScope,\n DiffRange,\n ChangedFilesOptions,\n} from './diff-scope';\nexport { AbstractRule } from './abstract-rule';\nexport {\n WEBPIECES_DISABLE,\n RULE_NAMES,\n hasDisable,\n WEBPIECES_TMP_DIR,\n MERGE_INFO_DIR,\n PR_REVIEW_DIR,\n MERGE_IN_PROGRESS_FILE,\n MERGE_EXPLANATION_FILE,\n} from './constants';\nexport { WebpiecesRulesConfig } from './WebpiecesRulesConfig';\nexport {\n SyncFlowGuidance,\n WP_START_UPDATE,\n WP_FINISH_UPDATE,\n WP_START_UPSERT_PR,\n WP_FINISH_UPSERT_PR,\n} from './sync-flow-guidance';\nexport {\n MaxMethodLinesConfig,\n MaxFileLinesConfig,\n RequireReturnTypeConfig,\n NoInlineTypeLiteralsConfig,\n NoAnyUnknownConfig,\n NoImplicitAnyConfig,\n PrismaValidateDtosConfig,\n PrismaConverterConfig,\n NoDestructureConfig,\n NoUnmanagedExceptionsConfig,\n CatchErrorPatternConfig,\n ThrowCauseRequiredConfig,\n AngularNoDirectApiInResolverConfig,\n NoSymbolDiTokensConfig,\n NoCustomCssConfig,\n NoProcessExitOutsideMainConfig,\n NoFunctionOutsideClassConfig,\n InjectAnnotationNotNeededForConcreteClassConfig,\n FrameworkTagConfig,\n RoleTagConfig,\n BranchCreationGuardConfig,\n PrCreationOrPushGuardConfig,\n MergeInProgressGuardConfig,\n PrMergeGuardConfig,\n RedirectHowToMergeMainConfig,\n FeatureBranchGuardConfig,\n ReadStaleGuardConfig,\n MergedBranchBashGuardConfig,\n NoFileImportCyclesConfig,\n RuntimeArchitectureConfig,\n NxWiringConfig,\n DiGraphConfig,\n NoJsFilesConfig,\n ValidateTsInSrcConfig,\n ValidateArchitectureUnchangedConfig,\n ValidateNoArchitectureCyclesConfig,\n ValidatePackageJsonConfig,\n ValidateVersionsLockedConfig,\n ValidateEslintSyncConfig,\n BaseRuleConfig,\n} from './rule-configs';\n// Mode unions + their value arrays — the single source of truth shared with code-rules.\nexport {\n METHOD_LIMIT_MODES,\n FILE_LIMIT_MODES,\n RETURN_TYPE_MODES,\n INLINE_TYPE_MODES,\n MODIFIED_CODE_MODES,\n PROJECT_MODES,\n PRISMA_DTOS_MODES,\n PRISMA_CONVERTER_MODES,\n DIRECT_API_RESOLVER_MODES,\n THROW_CAUSE_MODES,\n ON_OFF_MODES,\n STRUCTURAL_MODES,\n VALIDATE_TS_MODES,\n} from './rule-configs';\nexport type {\n MethodLimitMode,\n FileLimitMode,\n ReturnTypeMode,\n InlineTypeMode,\n ModifiedCodeMode,\n ProjectMode,\n PrismaValidateDtosMode,\n PrismaConverterMode,\n DirectApiResolverMode,\n ThrowCauseMode,\n OnOffMode,\n StructuralMode,\n ValidateTsMode,\n} from './rule-configs';\nexport {\n GateDefinition,\n PrGateConfig,\n defaultGates,\n defaultPrGateConfig,\n buildPrGateConfig,\n MERGE_MODE_AUTO,\n MERGE_MODE_NONE,\n MERGE_MODES,\n} from './pr-gate-config';\nexport {\n ChecklistDefinition,\n toChecklist,\n CHECKLIST_BLOCK,\n CHECKLIST_WARN,\n CHECKLIST_SEVERITIES,\n} from './checklist-config';\nexport type { RawChecklist } from './checklist-config';\nexport {\n ReviewJson,\n ChecklistAck,\n RequiredChecklist,\n ReviewJsonService,\n loadReviewJson,\n prDirFor,\n reviewJsonPath,\n reviewJsonSchemaHint,\n} from './review-json';\nexport {\n MainSyncStatus,\n MainSyncLock,\n MainSyncStatusService,\n DEFAULT_HANG_TIMEOUT_MINUTES,\n mainSyncStatusPath,\n mainSyncLockPath,\n readMainSyncStatus,\n writeMainSyncStatus,\n readMainSyncLock,\n writeMainSyncLock,\n isLockStale,\n isRefreshInProgress,\n inProcessLock,\n finishedLock,\n computeMainSyncStatus,\n stampCleanMainSyncStatus,\n squashRecoverySteps,\n} from './main-sync-status';\nexport {\n MergedBranch,\n DeletableBranch,\n DeletableWorktree,\n MergedBranchesCache,\n MergedBranchesService,\n} from './merged-branches';\nexport {\n Worktree,\n WorktreeService,\n} from './worktrees';\nexport {\n ReapedBranch,\n ReapResult,\n BranchReaper,\n} from './branch-reaper';\nexport type { MutationVerb, MutationPhase } from './branch-mutation-log';\nexport {\n BranchMutationEvent,\n BranchMutationLog,\n branchMutationLogPath,\n logBranchMutation,\n} from './branch-mutation-log';\nexport {\n CommandsConfig,\n buildCommandsConfig,\n DEFAULT_UPSERT_PR_COMMAND,\n DEFAULT_MERGE_COMPLETE_COMMAND,\n} from './commands-config';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/index.ts"],"names":[],"mappings":"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","sourcesContent":["export { ResolvedConfig, ResolvedRuleConfig, RuleOptions } from './types';\nexport { InformAiError } from './inform-ai-error';\nexport { RuleFailError } from './rule-fail-error';\nexport { CliExitError } from './cli-exit-error';\nexport { CliUsage, CliArgsCheck, CliArgs } from './cli-args';\nexport { runMain } from './run-main';\nexport { toError } from './to-error';\nexport { loadAndValidate, LoadedConfig, ConfigLoader } from './load-config';\nexport { findConfigFile, CONFIG_FILENAME, ConfigFile } from './config-file';\nexport { RepoRootFinder, INSTRUCT_AI_DIR } from './repo-root';\nexport { RulesConfigDesign } from './rules-config-design';\nexport { DocumentDesign, isDocumentDesign, DESIGN_METADATA_KEYS } from './di';\nexport { ExcludePaths } from './exclude-hook-paths';\nexport { isPathExcluded } from './exclude-paths';\nexport { defaultRules, defaultRulesDir } from './default-rules';\nexport { loadTemplate, writeTemplateIfMissing, writeTemplate, TemplateWriter } from './load-template';\nexport { validateWebpiecesConfig, validatePrGateSection, validateChecklists, validateSectionPlacement, validateCommandsSection, validateExcludePaths, validateMatchRulesSection, allRuleNames } from './validate-config';\nexport { validateChecklistDocs } from './checklist-docs-validator';\nexport {\n MatchRuleConfig,\n MatchRuleViolation,\n findMatchRuleViolations,\n isMatchRuleAllowedPath,\n compileMatchRulePatterns,\n renderMatchRuleMessage,\n DEFAULT_MATCH_RULES,\n} from './match-rules-config';\nexport type { ConfigSection } from './sections';\nexport { HOOK_GUARD_NAMES, isHookGuard, sectionForRule } from './sections';\nexport { FieldDef } from './field-def';\nexport type { SchemaShape } from './field-def';\nexport { shouldSkipRule, getCurrentBranch } from './skip-rule';\nexport type { SkipRuleResult } from './skip-rule';\nexport {\n detectBase,\n resolveBase,\n getChangedFiles,\n getFileDiff,\n getChangedLineNumbers,\n findNewMethodSignaturesInDiff,\n hasChangesInRange,\n isNewOrModified,\n DiffScope,\n DiffRange,\n ChangedFilesOptions,\n} from './diff-scope';\nexport { AbstractRule } from './abstract-rule';\nexport {\n WEBPIECES_DISABLE,\n RULE_NAMES,\n hasDisable,\n WEBPIECES_TMP_DIR,\n MERGE_INFO_DIR,\n PR_REVIEW_DIR,\n MERGE_IN_PROGRESS_FILE,\n MERGE_EXPLANATION_FILE,\n} from './constants';\nexport { WebpiecesRulesConfig } from './WebpiecesRulesConfig';\nexport {\n SyncFlowGuidance,\n WP_START_UPDATE,\n WP_FINISH_UPDATE,\n WP_START_UPSERT_PR,\n WP_FINISH_UPSERT_PR,\n} from './sync-flow-guidance';\nexport {\n MaxMethodLinesConfig,\n MaxFileLinesConfig,\n RequireReturnTypeConfig,\n NoInlineTypeLiteralsConfig,\n NoAnyUnknownConfig,\n NoImplicitAnyConfig,\n PrismaValidateDtosConfig,\n PrismaConverterConfig,\n NoDestructureConfig,\n NoUnmanagedExceptionsConfig,\n CatchErrorPatternConfig,\n ThrowCauseRequiredConfig,\n AngularNoDirectApiInResolverConfig,\n NoSymbolDiTokensConfig,\n NoCustomCssConfig,\n NoProcessExitOutsideMainConfig,\n NoFunctionOutsideClassConfig,\n InjectAnnotationNotNeededForConcreteClassConfig,\n FrameworkTagConfig,\n RoleTagConfig,\n BranchCreationGuardConfig,\n PrCreationOrPushGuardConfig,\n MergeInProgressGuardConfig,\n PrMergeGuardConfig,\n RedirectHowToMergeMainConfig,\n FeatureBranchGuardConfig,\n ReadStaleGuardConfig,\n MergedBranchBashGuardConfig,\n NoFileImportCyclesConfig,\n RuntimeArchitectureConfig,\n NxWiringConfig,\n DiGraphConfig,\n NoJsFilesConfig,\n ValidateTsInSrcConfig,\n ValidateArchitectureUnchangedConfig,\n ValidateNoArchitectureCyclesConfig,\n ValidatePackageJsonConfig,\n ValidateVersionsLockedConfig,\n ValidateEslintSyncConfig,\n BaseRuleConfig,\n} from './rule-configs';\n// Mode unions + their value arrays — the single source of truth shared with code-rules.\nexport {\n METHOD_LIMIT_MODES,\n FILE_LIMIT_MODES,\n RETURN_TYPE_MODES,\n INLINE_TYPE_MODES,\n MODIFIED_CODE_MODES,\n PROJECT_MODES,\n PRISMA_DTOS_MODES,\n PRISMA_CONVERTER_MODES,\n DIRECT_API_RESOLVER_MODES,\n THROW_CAUSE_MODES,\n ON_OFF_MODES,\n STRUCTURAL_MODES,\n VALIDATE_TS_MODES,\n} from './rule-configs';\nexport {\n NoClientCreationOutsideServerOrClientConfig,\n CLIENT_CREATION_SEVERITIES,\n} from './no-client-creation-config';\nexport type { ClientCreationSeverity } from './no-client-creation-config';\nexport type {\n MethodLimitMode,\n FileLimitMode,\n ReturnTypeMode,\n InlineTypeMode,\n ModifiedCodeMode,\n ProjectMode,\n PrismaValidateDtosMode,\n PrismaConverterMode,\n DirectApiResolverMode,\n ThrowCauseMode,\n OnOffMode,\n StructuralMode,\n ValidateTsMode,\n} from './rule-configs';\nexport {\n GateDefinition,\n PrGateConfig,\n defaultGates,\n defaultPrGateConfig,\n buildPrGateConfig,\n MERGE_MODE_AUTO,\n MERGE_MODE_NONE,\n MERGE_MODES,\n} from './pr-gate-config';\nexport {\n ChecklistDefinition,\n toChecklist,\n CHECKLIST_BLOCK,\n CHECKLIST_WARN,\n CHECKLIST_SEVERITIES,\n} from './checklist-config';\nexport type { RawChecklist } from './checklist-config';\nexport {\n GateTokenService,\n computeGateToken,\n gateTokenMarker,\n extractGateToken,\n verifyGateToken,\n} from './gate-token';\nexport {\n SubagentProvenanceService,\n ProvenanceResult,\n PROVENANCE_OK,\n PROVENANCE_MISSING,\n PROVENANCE_SKIPPED,\n} from './subagent-provenance';\nexport {\n ReviewJson,\n ChecklistAck,\n ChecklistResult,\n ChecklistVerdict,\n CK_PASS,\n CK_OVERRIDDEN,\n CK_FAIL,\n CK_MISSING,\n CK_ACKED,\n RequiredChecklist,\n ReviewJsonService,\n loadReviewJson,\n prDirFor,\n reviewJsonPath,\n reviewJsonSchemaHint,\n} from './review-json';\nexport {\n MainSyncStatus,\n MainSyncLock,\n MainSyncStatusService,\n DEFAULT_HANG_TIMEOUT_MINUTES,\n mainSyncStatusPath,\n mainSyncLockPath,\n readMainSyncStatus,\n writeMainSyncStatus,\n readMainSyncLock,\n writeMainSyncLock,\n isLockStale,\n isRefreshInProgress,\n inProcessLock,\n finishedLock,\n computeMainSyncStatus,\n stampCleanMainSyncStatus,\n squashRecoverySteps,\n} from './main-sync-status';\nexport {\n MergedBranch,\n DeletableBranch,\n DeletableWorktree,\n MergedBranchesCache,\n MergedBranchesService,\n} from './merged-branches';\nexport {\n Worktree,\n WorktreeService,\n} from './worktrees';\nexport {\n ReapedBranch,\n ReapResult,\n BranchReaper,\n} from './branch-reaper';\nexport type { MutationVerb, MutationPhase } from './branch-mutation-log';\nexport {\n BranchMutationEvent,\n BranchMutationLog,\n branchMutationLogPath,\n logBranchMutation,\n} from './branch-mutation-log';\nexport {\n CommandsConfig,\n buildCommandsConfig,\n DEFAULT_UPSERT_PR_COMMAND,\n DEFAULT_MERGE_COMPLETE_COMMAND,\n} from './commands-config';\n"]}
@@ -0,0 +1,12 @@
1
+ import { SchemaShape } from './field-def';
2
+ import { BaseRuleConfig, ModifiedCodeMode } from './rule-configs';
3
+ export declare const CLIENT_CREATION_SEVERITIES: readonly ["warn", "error"];
4
+ export type ClientCreationSeverity = typeof CLIENT_CREATION_SEVERITIES[number];
5
+ export declare class NoClientCreationOutsideServerOrClientConfig extends BaseRuleConfig {
6
+ mode?: ModifiedCodeMode;
7
+ severity?: ClientCreationSeverity;
8
+ disableAllowed?: boolean;
9
+ allowedRoles?: string[];
10
+ allowedPaths?: string[];
11
+ static readonly SCHEMA: SchemaShape<NoClientCreationOutsideServerOrClientConfig>;
12
+ }
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NoClientCreationOutsideServerOrClientConfig = exports.CLIENT_CREATION_SEVERITIES = void 0;
4
+ const field_def_1 = require("./field-def");
5
+ const rule_configs_1 = require("./rule-configs");
6
+ // no-client-creation-outside-server-or-client severity. Landed as a hard failure this rule would
7
+ // break every existing Angular repo on upgrade (provideCoreClient-style helpers create the client
8
+ // inside a role:lib on purpose), so it ships WARN — reports + prints the migration but passes the
9
+ // build — and a repo flips it to `error` once it has migrated its libraries.
10
+ exports.CLIENT_CREATION_SEVERITIES = ['warn', 'error'];
11
+ // no-client-creation-outside-server-or-client — flags a project that CONSTRUCTS an rpc/pubsub client
12
+ // (`factory.createRpcClient(...)` / `factory.createPubSubClient(...)`) when the project's `role:` tag
13
+ // is not one of `allowedRoles`. Only a runnable entrypoint (server / client app / app) has a declared
14
+ // identity (`serviceName`) or target (`callsService`), so a client built there is attributable in the
15
+ // runtime graph; a client built inside a `role:lib` reaches the fan-out fallback (one `uses` edge to
16
+ // EVERY implementer of the api) and draws calls that cannot happen. A reusable library takes the api
17
+ // INJECTED — the server/app module binds it to a client. Importing the api type or its DI token is
18
+ // FINE and never flagged; only constructing the transport is.
19
+ //
20
+ // `severity` ships "warn" (report + migration, build passes) so an upgrade can't break an un-migrated
21
+ // Angular repo; flip to "error" once libraries are migrated. `allowedRoles` defaults to the runnable
22
+ // roles; `allowedPaths` exempts whole file trees (shared glob semantics). Standard rollout knobs via
23
+ // the base: mode (OFF | NEW_AND_MODIFIED_CODE | NEW_AND_MODIFIED_FILES), ignoreModifiedUntilEpoch,
24
+ // branch, and disableAllowed for the inline `// webpieces-disable` escape.
25
+ class NoClientCreationOutsideServerOrClientConfig extends rule_configs_1.BaseRuleConfig {
26
+ severity;
27
+ disableAllowed;
28
+ allowedRoles;
29
+ allowedPaths;
30
+ static SCHEMA = {
31
+ mode: new field_def_1.FieldDef('string', rule_configs_1.MODIFIED_CODE_MODES),
32
+ severity: field_def_1.FieldDef.optional('string', exports.CLIENT_CREATION_SEVERITIES),
33
+ disableAllowed: field_def_1.FieldDef.optional('boolean'),
34
+ allowedRoles: field_def_1.FieldDef.optional('string[]'),
35
+ allowedPaths: field_def_1.FieldDef.optional('string[]'),
36
+ ...rule_configs_1.BASE_RULE_SCHEMA,
37
+ };
38
+ }
39
+ exports.NoClientCreationOutsideServerOrClientConfig = NoClientCreationOutsideServerOrClientConfig;
40
+ //# sourceMappingURL=no-client-creation-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-client-creation-config.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/no-client-creation-config.ts"],"names":[],"mappings":";;;AAAA,2CAAoD;AACpD,iDAAyG;AAEzG,iGAAiG;AACjG,kGAAkG;AAClG,kGAAkG;AAClG,6EAA6E;AAChE,QAAA,0BAA0B,GAAG,CAAC,MAAM,EAAE,OAAO,CAAU,CAAC;AAGrE,qGAAqG;AACrG,sGAAsG;AACtG,sGAAsG;AACtG,sGAAsG;AACtG,qGAAqG;AACrG,qGAAqG;AACrG,mGAAmG;AACnG,8DAA8D;AAC9D,EAAE;AACF,sGAAsG;AACtG,qGAAqG;AACrG,qGAAqG;AACrG,mGAAmG;AACnG,2EAA2E;AAC3E,MAAa,2CAA4C,SAAQ,6BAAc;IAE3E,QAAQ,CAA0B;IAClC,cAAc,CAAW;IACzB,YAAY,CAAY;IACxB,YAAY,CAAY;IAExB,MAAM,CAAU,MAAM,GAA6D;QAC/E,IAAI,EAAE,IAAI,oBAAQ,CAAC,QAAQ,EAAE,kCAAmB,CAAC;QACjD,QAAQ,EAAE,oBAAQ,CAAC,QAAQ,CAAC,QAAQ,EAAE,kCAA0B,CAAC;QACjE,cAAc,EAAE,oBAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC;QAC5C,YAAY,EAAE,oBAAQ,CAAC,QAAQ,CAAC,UAAU,CAAC;QAC3C,YAAY,EAAE,oBAAQ,CAAC,QAAQ,CAAC,UAAU,CAAC;QAC3C,GAAG,+BAAgB;KACtB,CAAC;;AAdN,kGAeC","sourcesContent":["import { FieldDef, SchemaShape } from './field-def';\nimport { BaseRuleConfig, BASE_RULE_SCHEMA, ModifiedCodeMode, MODIFIED_CODE_MODES } from './rule-configs';\n\n// no-client-creation-outside-server-or-client severity. Landed as a hard failure this rule would\n// break every existing Angular repo on upgrade (provideCoreClient-style helpers create the client\n// inside a role:lib on purpose), so it ships WARN — reports + prints the migration but passes the\n// build — and a repo flips it to `error` once it has migrated its libraries.\nexport const CLIENT_CREATION_SEVERITIES = ['warn', 'error'] as const;\nexport type ClientCreationSeverity = typeof CLIENT_CREATION_SEVERITIES[number];\n\n// no-client-creation-outside-server-or-client — flags a project that CONSTRUCTS an rpc/pubsub client\n// (`factory.createRpcClient(...)` / `factory.createPubSubClient(...)`) when the project's `role:` tag\n// is not one of `allowedRoles`. Only a runnable entrypoint (server / client app / app) has a declared\n// identity (`serviceName`) or target (`callsService`), so a client built there is attributable in the\n// runtime graph; a client built inside a `role:lib` reaches the fan-out fallback (one `uses` edge to\n// EVERY implementer of the api) and draws calls that cannot happen. A reusable library takes the api\n// INJECTED — the server/app module binds it to a client. Importing the api type or its DI token is\n// FINE and never flagged; only constructing the transport is.\n//\n// `severity` ships \"warn\" (report + migration, build passes) so an upgrade can't break an un-migrated\n// Angular repo; flip to \"error\" once libraries are migrated. `allowedRoles` defaults to the runnable\n// roles; `allowedPaths` exempts whole file trees (shared glob semantics). Standard rollout knobs via\n// the base: mode (OFF | NEW_AND_MODIFIED_CODE | NEW_AND_MODIFIED_FILES), ignoreModifiedUntilEpoch,\n// branch, and disableAllowed for the inline `// webpieces-disable` escape.\nexport class NoClientCreationOutsideServerOrClientConfig extends BaseRuleConfig {\n declare mode?: ModifiedCodeMode;\n severity?: ClientCreationSeverity;\n disableAllowed?: boolean;\n allowedRoles?: string[];\n allowedPaths?: string[];\n\n static readonly SCHEMA: SchemaShape<NoClientCreationOutsideServerOrClientConfig> = {\n mode: new FieldDef('string', MODIFIED_CODE_MODES),\n severity: FieldDef.optional('string', CLIENT_CREATION_SEVERITIES),\n disableAllowed: FieldDef.optional('boolean'),\n allowedRoles: FieldDef.optional('string[]'),\n allowedPaths: FieldDef.optional('string[]'),\n ...BASE_RULE_SCHEMA,\n };\n}\n"]}
@@ -29,7 +29,20 @@ export declare class PrGateConfig {
29
29
  */
30
30
  mergeMode: string;
31
31
  checklists: ChecklistDefinition[];
32
- constructor(mode: string, buildCommand: string, gates: GateDefinition[], mergeMode: string, checklists?: ChecklistDefinition[]);
32
+ /**
33
+ * Shared secret used to mint the server-verifiable gate token. `wp-finish-upsert-pr` writes
34
+ * `HMAC(gateSalt, HEAD_sha)` as a hidden marker into the PR body (and REFUSES to mint it unless
35
+ * every BLOCK checklist passed), so a valid token IS proof the local gate ran and passed. A CI
36
+ * check (`wp-check-pr` + the scaffolded workflow) recomputes it from the PR head sha and this salt.
37
+ *
38
+ * Optional, defaults to '' — empty means "no token minted, no CI enforcement" (byte-identical to
39
+ * before this field existed). This is COMMITTED, obscurity-grade: it stops unhooked teammates who
40
+ * push + open a PR in the web UI, but is readable in-repo and therefore forgeable by a determined
41
+ * reader. It is deliberately NOT cryptographically sound; nothing local can stop a filesystem-reading
42
+ * agent. See RESPONSE-pr-gate-ci-enforcement / the design memo for the full tradeoff.
43
+ */
44
+ gateSalt: string;
45
+ constructor(mode: string, buildCommand: string, gates: GateDefinition[], mergeMode: string, checklists?: ChecklistDefinition[], gateSalt?: string);
33
46
  }
34
47
  export declare function defaultGates(): GateDefinition[];
35
48
  export declare function defaultPrGateConfig(): PrGateConfig;
@@ -58,13 +58,27 @@ class PrGateConfig {
58
58
  // Diff-triggered company review checklists (the extension point). Defaults to [] — an absent
59
59
  // `checklists` key produces byte-identical behavior to before this field existed.
60
60
  checklists;
61
+ /**
62
+ * Shared secret used to mint the server-verifiable gate token. `wp-finish-upsert-pr` writes
63
+ * `HMAC(gateSalt, HEAD_sha)` as a hidden marker into the PR body (and REFUSES to mint it unless
64
+ * every BLOCK checklist passed), so a valid token IS proof the local gate ran and passed. A CI
65
+ * check (`wp-check-pr` + the scaffolded workflow) recomputes it from the PR head sha and this salt.
66
+ *
67
+ * Optional, defaults to '' — empty means "no token minted, no CI enforcement" (byte-identical to
68
+ * before this field existed). This is COMMITTED, obscurity-grade: it stops unhooked teammates who
69
+ * push + open a PR in the web UI, but is readable in-repo and therefore forgeable by a determined
70
+ * reader. It is deliberately NOT cryptographically sound; nothing local can stop a filesystem-reading
71
+ * agent. See RESPONSE-pr-gate-ci-enforcement / the design memo for the full tradeoff.
72
+ */
73
+ gateSalt;
61
74
  // eslint-disable-next-line @typescript-eslint/max-params
62
- constructor(mode, buildCommand, gates, mergeMode, checklists = []) {
75
+ constructor(mode, buildCommand, gates, mergeMode, checklists = [], gateSalt = '') {
63
76
  this.mode = mode;
64
77
  this.buildCommand = buildCommand;
65
78
  this.gates = gates;
66
79
  this.mergeMode = mergeMode;
67
80
  this.checklists = checklists;
81
+ this.gateSalt = gateSalt;
68
82
  }
69
83
  }
70
84
  exports.PrGateConfig = PrGateConfig;
@@ -105,6 +119,8 @@ function buildPrGateConfig(section) {
105
119
  const mergeMode = raw.mergeMode ?? defaults.mergeMode;
106
120
  // Optional extension point — omitted ⇒ [] ⇒ no checklists computed anywhere downstream.
107
121
  const checklists = raw.checklists !== undefined ? raw.checklists.map(checklist_config_1.toChecklist) : defaults.checklists;
108
- return new PrGateConfig(mode, buildCommand, gates, mergeMode, checklists);
122
+ // Optional omitted '' ⇒ no gate token minted and CI enforcement is a no-op (back-compat).
123
+ const gateSalt = raw.gateSalt ?? defaults.gateSalt;
124
+ return new PrGateConfig(mode, buildCommand, gates, mergeMode, checklists, gateSalt);
109
125
  }
110
126
  //# sourceMappingURL=pr-gate-config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pr-gate-config.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/pr-gate-config.ts"],"names":[],"mappings":"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","sourcesContent":["// PrGateConfig is the \"special section\" for the pr-gate dashboard. It does NOT live in the\n// validated `rules` map (the FieldDef schema can't express nested object arrays), but as a\n// top-level `pr-gate` key in webpieces.config.json. It is built and validated by\n// loadAndValidate (load-config.ts); this module holds only the data classes + defaults + toGate.\n\nimport { ChecklistDefinition, RawChecklist, toChecklist } from './checklist-config';\n\nexport class GateDefinition {\n name: string;\n patterns: string[];\n // The warning color shown on the dashboard WHEN this gate's patterns match a changed file. Green\n // is implicit (shown when nothing matched), so it is never configured. warningColor is purely\n // visual — even 'red' never fails/blocks the PR (only the build gate can). 'yellow' = caution,\n // 'red' = louder \"look here\" flag (e.g. DB schema / migration changes). REQUIRED on every gate.\n warningColor: string; // 'yellow' | 'red'\n // Example/inactive gate: parsed and kept in the file (JSON has no comments) but skipped at\n // compute/render time. Other projects flip this to false and tune patterns/warningColor.\n disabled: boolean;\n\n constructor(name: string, patterns: string[], warningColor: string, disabled = false) {\n this.name = name;\n this.patterns = patterns;\n this.warningColor = warningColor;\n this.disabled = disabled;\n }\n}\n\n// How wp-finish-upsert-pr should try to LAND the PR once it has posted it. GitHub's auto-merge queue\n// is a REPO-level setting (`allow_auto_merge`) that many orgs turn OFF as a policy control, and it\n// CANNOT be forced from the client: `gh pr merge --auto` calls the enablePullRequestAutoMerge GraphQL\n// mutation, which hard-errors with \"Auto merge is not allowed for this repository\" when the repo says\n// no. So the only lever a config knob has is WHICH PATHS WE ATTEMPT — never whether the queue exists.\nexport const MERGE_MODE_AUTO = 'AUTO';\nexport const MERGE_MODE_NONE = 'NONE';\nexport const MERGE_MODES = [MERGE_MODE_AUTO, MERGE_MODE_NONE];\n\nexport class PrGateConfig {\n mode: string;\n buildCommand: string;\n gates: GateDefinition[];\n /**\n * REQUIRED — every repo must state its policy; there is deliberately no default, because the two\n * answers are a real policy decision and guessing it either merges when a team did not want that,\n * or silently stops landing PRs on a team that relied on it.\n *\n * AUTO — wp-finish-upsert-pr LANDS the PR: squash-merge it right away when it is mergeable, else\n * enable GitHub auto-merge so it lands when the checks pass. Both carry an explicit --subject /\n * --body-file, which is the ONLY way main's history gets the PR title plus the compact\n * risk/flags body — no repo setting can produce that. Requires allow_auto_merge on the repo.\n * NONE — wp-finish-upsert-pr only opens/updates the PR and stops; a human merges. NOTE the cost:\n * a UI merge cannot use the compact body, so main's commit falls back to the repo's\n * squash_merge_commit_title/message settings. Set squash_merge_commit_title=PR_TITLE there, or\n * commits land as the internal \"Squash merge of <branch>\" subject.\n */\n mergeMode: string;\n // Diff-triggered company review checklists (the extension point). Defaults to [] — an absent\n // `checklists` key produces byte-identical behavior to before this field existed.\n checklists: ChecklistDefinition[];\n\n // eslint-disable-next-line @typescript-eslint/max-params\n constructor(mode: string, buildCommand: string, gates: GateDefinition[], mergeMode: string, checklists: ChecklistDefinition[] = []) {\n this.mode = mode;\n this.buildCommand = buildCommand;\n this.gates = gates;\n this.mergeMode = mergeMode;\n this.checklists = checklists;\n }\n}\n\n// Default infra gates — path-pattern based, tuned for this monorepo. Clients override the\n// whole list via the `pr-gate.gates` array in webpieces.config.json.\nexport function defaultGates(): GateDefinition[] {\n return [\n new GateDefinition('API Changed', ['libraries/apis/**', '**/*Api.ts'], 'yellow'),\n new GateDefinition('Config Files Changed', ['**/package.json', '**/tsconfig*.json', 'nx.json', '**/*.config.*'], 'yellow'),\n new GateDefinition('Dependency Graph Changed', ['architecture/dependencies.json'], 'yellow'),\n new GateDefinition('Claude / Rules Changed', ['**/CLAUDE.md', '**/claude.*.md', '.claude/**', 'webpieces.config.json'], 'yellow'),\n ];\n}\n\nexport function defaultPrGateConfig(): PrGateConfig {\n // No default checklists — the extension point is opt-in; the default monorepo ships none.\n return new PrGateConfig('ON', '', defaultGates(), MERGE_MODE_AUTO, []);\n}\n\ninterface RawGate {\n name?: string;\n patterns?: string[];\n warningColor?: string;\n disabled?: boolean;\n}\n\ninterface RawPrGateSection {\n mode?: string;\n buildCommand?: string;\n gates?: RawGate[];\n mergeMode?: string;\n checklists?: RawChecklist[];\n}\n\nfunction toGate(raw: RawGate): GateDefinition {\n return new GateDefinition(raw.name ?? '', raw.patterns ?? [], raw.warningColor ?? 'yellow', raw.disabled ?? false);\n}\n\n/**\n * Build a PrGateConfig from the already-parsed top-level `pr-gate` section, falling back to defaults\n * for any field the consumer omits. Pure transform — the file read + structural validation happen in\n * loadAndValidate (load-config.ts) so every consumer goes through one validated path. Pass undefined\n * (no `pr-gate` key / no config file) to get full defaults.\n */\n// webpieces-disable no-any-unknown -- `section` is opaque consumer JSON until narrowed here\nexport function buildPrGateConfig(section: unknown): PrGateConfig {\n const defaults = defaultPrGateConfig();\n if (section === undefined || section === null || typeof section !== 'object') return defaults;\n\n const raw = section as RawPrGateSection;\n const mode = raw.mode ?? defaults.mode;\n const buildCommand = raw.buildCommand ?? defaults.buildCommand;\n const gates = raw.gates !== undefined ? raw.gates.map(toGate) : defaults.gates;\n // REQUIRED — validatePrGateSection rejects an omitted/unknown value, so this fallback only ever\n // applies to the no-config-file path that defaultPrGateConfig() serves.\n const mergeMode = raw.mergeMode ?? defaults.mergeMode;\n // Optional extension point — omitted ⇒ [] ⇒ no checklists computed anywhere downstream.\n const checklists = raw.checklists !== undefined ? raw.checklists.map(toChecklist) : defaults.checklists;\n return new PrGateConfig(mode, buildCommand, gates, mergeMode, checklists);\n}\n"]}
1
+ {"version":3,"file":"pr-gate-config.js","sourceRoot":"","sources":["../../../../../packages/tooling/rules-config/src/pr-gate-config.ts"],"names":[],"mappings":"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","sourcesContent":["// PrGateConfig is the \"special section\" for the pr-gate dashboard. It does NOT live in the\n// validated `rules` map (the FieldDef schema can't express nested object arrays), but as a\n// top-level `pr-gate` key in webpieces.config.json. It is built and validated by\n// loadAndValidate (load-config.ts); this module holds only the data classes + defaults + toGate.\n\nimport { ChecklistDefinition, RawChecklist, toChecklist } from './checklist-config';\n\nexport class GateDefinition {\n name: string;\n patterns: string[];\n // The warning color shown on the dashboard WHEN this gate's patterns match a changed file. Green\n // is implicit (shown when nothing matched), so it is never configured. warningColor is purely\n // visual — even 'red' never fails/blocks the PR (only the build gate can). 'yellow' = caution,\n // 'red' = louder \"look here\" flag (e.g. DB schema / migration changes). REQUIRED on every gate.\n warningColor: string; // 'yellow' | 'red'\n // Example/inactive gate: parsed and kept in the file (JSON has no comments) but skipped at\n // compute/render time. Other projects flip this to false and tune patterns/warningColor.\n disabled: boolean;\n\n constructor(name: string, patterns: string[], warningColor: string, disabled = false) {\n this.name = name;\n this.patterns = patterns;\n this.warningColor = warningColor;\n this.disabled = disabled;\n }\n}\n\n// How wp-finish-upsert-pr should try to LAND the PR once it has posted it. GitHub's auto-merge queue\n// is a REPO-level setting (`allow_auto_merge`) that many orgs turn OFF as a policy control, and it\n// CANNOT be forced from the client: `gh pr merge --auto` calls the enablePullRequestAutoMerge GraphQL\n// mutation, which hard-errors with \"Auto merge is not allowed for this repository\" when the repo says\n// no. So the only lever a config knob has is WHICH PATHS WE ATTEMPT — never whether the queue exists.\nexport const MERGE_MODE_AUTO = 'AUTO';\nexport const MERGE_MODE_NONE = 'NONE';\nexport const MERGE_MODES = [MERGE_MODE_AUTO, MERGE_MODE_NONE];\n\nexport class PrGateConfig {\n mode: string;\n buildCommand: string;\n gates: GateDefinition[];\n /**\n * REQUIRED — every repo must state its policy; there is deliberately no default, because the two\n * answers are a real policy decision and guessing it either merges when a team did not want that,\n * or silently stops landing PRs on a team that relied on it.\n *\n * AUTO — wp-finish-upsert-pr LANDS the PR: squash-merge it right away when it is mergeable, else\n * enable GitHub auto-merge so it lands when the checks pass. Both carry an explicit --subject /\n * --body-file, which is the ONLY way main's history gets the PR title plus the compact\n * risk/flags body — no repo setting can produce that. Requires allow_auto_merge on the repo.\n * NONE — wp-finish-upsert-pr only opens/updates the PR and stops; a human merges. NOTE the cost:\n * a UI merge cannot use the compact body, so main's commit falls back to the repo's\n * squash_merge_commit_title/message settings. Set squash_merge_commit_title=PR_TITLE there, or\n * commits land as the internal \"Squash merge of <branch>\" subject.\n */\n mergeMode: string;\n // Diff-triggered company review checklists (the extension point). Defaults to [] — an absent\n // `checklists` key produces byte-identical behavior to before this field existed.\n checklists: ChecklistDefinition[];\n /**\n * Shared secret used to mint the server-verifiable gate token. `wp-finish-upsert-pr` writes\n * `HMAC(gateSalt, HEAD_sha)` as a hidden marker into the PR body (and REFUSES to mint it unless\n * every BLOCK checklist passed), so a valid token IS proof the local gate ran and passed. A CI\n * check (`wp-check-pr` + the scaffolded workflow) recomputes it from the PR head sha and this salt.\n *\n * Optional, defaults to '' — empty means \"no token minted, no CI enforcement\" (byte-identical to\n * before this field existed). This is COMMITTED, obscurity-grade: it stops unhooked teammates who\n * push + open a PR in the web UI, but is readable in-repo and therefore forgeable by a determined\n * reader. It is deliberately NOT cryptographically sound; nothing local can stop a filesystem-reading\n * agent. See RESPONSE-pr-gate-ci-enforcement / the design memo for the full tradeoff.\n */\n gateSalt: string;\n\n // eslint-disable-next-line @typescript-eslint/max-params\n constructor(mode: string, buildCommand: string, gates: GateDefinition[], mergeMode: string, checklists: ChecklistDefinition[] = [], gateSalt = '') {\n this.mode = mode;\n this.buildCommand = buildCommand;\n this.gates = gates;\n this.mergeMode = mergeMode;\n this.checklists = checklists;\n this.gateSalt = gateSalt;\n }\n}\n\n// Default infra gates — path-pattern based, tuned for this monorepo. Clients override the\n// whole list via the `pr-gate.gates` array in webpieces.config.json.\nexport function defaultGates(): GateDefinition[] {\n return [\n new GateDefinition('API Changed', ['libraries/apis/**', '**/*Api.ts'], 'yellow'),\n new GateDefinition('Config Files Changed', ['**/package.json', '**/tsconfig*.json', 'nx.json', '**/*.config.*'], 'yellow'),\n new GateDefinition('Dependency Graph Changed', ['architecture/dependencies.json'], 'yellow'),\n new GateDefinition('Claude / Rules Changed', ['**/CLAUDE.md', '**/claude.*.md', '.claude/**', 'webpieces.config.json'], 'yellow'),\n ];\n}\n\nexport function defaultPrGateConfig(): PrGateConfig {\n // No default checklists — the extension point is opt-in; the default monorepo ships none.\n return new PrGateConfig('ON', '', defaultGates(), MERGE_MODE_AUTO, []);\n}\n\ninterface RawGate {\n name?: string;\n patterns?: string[];\n warningColor?: string;\n disabled?: boolean;\n}\n\ninterface RawPrGateSection {\n mode?: string;\n buildCommand?: string;\n gates?: RawGate[];\n mergeMode?: string;\n checklists?: RawChecklist[];\n gateSalt?: string;\n}\n\nfunction toGate(raw: RawGate): GateDefinition {\n return new GateDefinition(raw.name ?? '', raw.patterns ?? [], raw.warningColor ?? 'yellow', raw.disabled ?? false);\n}\n\n/**\n * Build a PrGateConfig from the already-parsed top-level `pr-gate` section, falling back to defaults\n * for any field the consumer omits. Pure transform — the file read + structural validation happen in\n * loadAndValidate (load-config.ts) so every consumer goes through one validated path. Pass undefined\n * (no `pr-gate` key / no config file) to get full defaults.\n */\n// webpieces-disable no-any-unknown -- `section` is opaque consumer JSON until narrowed here\nexport function buildPrGateConfig(section: unknown): PrGateConfig {\n const defaults = defaultPrGateConfig();\n if (section === undefined || section === null || typeof section !== 'object') return defaults;\n\n const raw = section as RawPrGateSection;\n const mode = raw.mode ?? defaults.mode;\n const buildCommand = raw.buildCommand ?? defaults.buildCommand;\n const gates = raw.gates !== undefined ? raw.gates.map(toGate) : defaults.gates;\n // REQUIRED — validatePrGateSection rejects an omitted/unknown value, so this fallback only ever\n // applies to the no-config-file path that defaultPrGateConfig() serves.\n const mergeMode = raw.mergeMode ?? defaults.mergeMode;\n // Optional extension point — omitted ⇒ [] ⇒ no checklists computed anywhere downstream.\n const checklists = raw.checklists !== undefined ? raw.checklists.map(toChecklist) : defaults.checklists;\n // Optional — omitted ⇒ '' ⇒ no gate token minted and CI enforcement is a no-op (back-compat).\n const gateSalt = raw.gateSalt ?? defaults.gateSalt;\n return new PrGateConfig(mode, buildCommand, gates, mergeMode, checklists, gateSalt);\n}\n"]}
@@ -4,6 +4,13 @@ export declare class ChecklistAck {
4
4
  notes: string[];
5
5
  constructor(id: string, acknowledged: boolean, notes: string[]);
6
6
  }
7
+ export declare class ChecklistResult {
8
+ id: string;
9
+ success: boolean;
10
+ output: string;
11
+ override: string;
12
+ constructor(id: string, success: boolean, output: string, override: string);
13
+ }
7
14
  export declare class RequiredChecklist {
8
15
  id: string;
9
16
  title: string;
@@ -11,7 +18,8 @@ export declare class RequiredChecklist {
11
18
  docs: string[];
12
19
  blockMessage: string;
13
20
  matchedFiles: string[];
14
- constructor(id: string, title: string, severity: string, docs: string[], blockMessage: string, matchedFiles: string[]);
21
+ subagent: string;
22
+ constructor(id: string, title: string, severity: string, docs: string[], blockMessage: string, matchedFiles: string[], subagent?: string);
15
23
  }
16
24
  export declare class ReviewJson {
17
25
  title: string;
@@ -23,13 +31,26 @@ export declare class ReviewJson {
23
31
  risks: string[];
24
32
  filesToReview: string[];
25
33
  checklists: ChecklistAck[];
26
- constructor(title: string, riskScore: number, riskLevel: string, riskEmoji: string, summary: string, violations: string[], risks: string[], filesToReview: string[], checklists?: ChecklistAck[]);
34
+ results: ChecklistResult[];
35
+ constructor(title: string, riskScore: number, riskLevel: string, riskEmoji: string, summary: string, violations: string[], risks: string[], filesToReview: string[], checklists?: ChecklistAck[], results?: ChecklistResult[]);
36
+ }
37
+ export declare const CK_PASS = "pass";
38
+ export declare const CK_OVERRIDDEN = "overridden";
39
+ export declare const CK_FAIL = "fail";
40
+ export declare const CK_MISSING = "missing";
41
+ export declare const CK_ACKED = "acknowledged";
42
+ export declare class ChecklistVerdict {
43
+ id: string;
44
+ status: string;
45
+ detail: string;
46
+ constructor(id: string, status: string, detail: string);
27
47
  }
28
48
  /** Locates + loads/validates the AI-authored review.json. `@injectable(bindingScopeValues.Singleton)` so it's drawn in the design. */
29
49
  export declare class ReviewJsonService {
30
50
  prDirFor(repoRoot: string, featureName: string): string;
31
51
  reviewJsonPath(repoRoot: string, featureName: string): string;
32
52
  reviewJsonSchemaHint(filePath: string, required?: readonly RequiredChecklist[]): string;
53
+ checklistResultPath(reviewJsonFilePath: string, checklistId: string): string;
33
54
  private requiredChecklistHint;
34
55
  /**
35
56
  * Load + validate the AI-authored review.json. Throws InformAiError (with the schema) when missing,
@@ -43,7 +64,11 @@ export declare class ReviewJsonService {
43
64
  */
44
65
  loadReviewJson(filePath: string, required?: readonly RequiredChecklist[]): ReviewJson;
45
66
  private parseChecklistAcks;
67
+ loadChecklistResults(reviewJsonFilePath: string, required: readonly RequiredChecklist[]): ChecklistResult[];
68
+ resolveVerdict(req: RequiredChecklist, acks: readonly ChecklistAck[], results: readonly ChecklistResult[]): ChecklistVerdict;
46
69
  private requiredChecklistErrors;
70
+ private checklistFileName;
71
+ private parseChecklistResult;
47
72
  private asStringArray;
48
73
  private parseReviewJson;
49
74
  }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ReviewJsonService = exports.ReviewJson = exports.RequiredChecklist = exports.ChecklistAck = void 0;
3
+ exports.ReviewJsonService = exports.ChecklistVerdict = exports.CK_ACKED = exports.CK_MISSING = exports.CK_FAIL = exports.CK_OVERRIDDEN = exports.CK_PASS = exports.ReviewJson = exports.RequiredChecklist = exports.ChecklistResult = exports.ChecklistAck = void 0;
4
4
  exports.prDirFor = prDirFor;
5
5
  exports.reviewJsonPath = reviewJsonPath;
6
6
  exports.reviewJsonSchemaHint = reviewJsonSchemaHint;
@@ -26,6 +26,27 @@ class ChecklistAck {
26
26
  }
27
27
  }
28
28
  exports.ChecklistAck = ChecklistAck;
29
+ // The verdict a reviewer wrote into a PER-CHECKLIST file `.webpieces/pr-review/<branch>/review-<id>.json`,
30
+ // one per triggered checklist. Replaces the single shared `checklists[]` array so concurrent reviewer
31
+ // subagents never clobber one file. Unlike ChecklistAck's bare boolean, this records the OUTCOME:
32
+ // success:true → PASS
33
+ // success:false + override non-empty → OVERRIDDEN (pass; the free-text justification reaches the PR)
34
+ // success:false + no override → FAIL (refuse; `output` is printed verbatim)
35
+ // `override` is deliberately free text, not a boolean — it forces the bypass to be stated in words and
36
+ // surfaces it on the dashboard, where a human sees it. Data-only (per CLAUDE.md).
37
+ class ChecklistResult {
38
+ id;
39
+ success;
40
+ output; // what the reviewer found; printed verbatim when a BLOCK checklist fails
41
+ override; // '' = no override; non-empty = ship-anyway justification (renders 🟡 overridden)
42
+ constructor(id, success, output, override) {
43
+ this.id = id;
44
+ this.success = success;
45
+ this.output = output;
46
+ this.override = override;
47
+ }
48
+ }
49
+ exports.ChecklistResult = ChecklistResult;
29
50
  // What the CALLER (the pr-gate command) computed from the diff: the checklists this branch triggered.
30
51
  // Drives BOTH review.json validation (BLOCK must be acknowledged) AND the printed schema hint (so the
31
52
  // AI is told, at the moment it writes review.json, exactly which docs to read). Data-only.
@@ -36,14 +57,16 @@ class RequiredChecklist {
36
57
  docs;
37
58
  blockMessage;
38
59
  matchedFiles; // the changed files that triggered it (for the dashboard + hint)
60
+ subagent; // expected reviewer agentType ('' = no provenance requirement); enforced by finish
39
61
  // eslint-disable-next-line @typescript-eslint/max-params
40
- constructor(id, title, severity, docs, blockMessage, matchedFiles) {
62
+ constructor(id, title, severity, docs, blockMessage, matchedFiles, subagent = '') {
41
63
  this.id = id;
42
64
  this.title = title;
43
65
  this.severity = severity;
44
66
  this.docs = docs;
45
67
  this.blockMessage = blockMessage;
46
68
  this.matchedFiles = matchedFiles;
69
+ this.subagent = subagent;
47
70
  }
48
71
  }
49
72
  exports.RequiredChecklist = RequiredChecklist;
@@ -58,9 +81,10 @@ class ReviewJson {
58
81
  violations; // pattern/architecture violations; length = the Pattern Violations count
59
82
  risks;
60
83
  filesToReview;
61
- checklists; // consumer-checklist acknowledgments; [] when no checklists were required
84
+ checklists; // legacy inline acknowledgments (back-compat); [] when none written
85
+ results; // resolved per-checklist verdicts (from review-<id>.json); [] when none
62
86
  // eslint-disable-next-line @typescript-eslint/max-params
63
- constructor(title, riskScore, riskLevel, riskEmoji, summary, violations, risks, filesToReview, checklists = []) {
87
+ constructor(title, riskScore, riskLevel, riskEmoji, summary, violations, risks, filesToReview, checklists = [], results = []) {
64
88
  this.title = title;
65
89
  this.riskScore = riskScore;
66
90
  this.riskLevel = riskLevel;
@@ -70,9 +94,27 @@ class ReviewJson {
70
94
  this.risks = risks;
71
95
  this.filesToReview = filesToReview;
72
96
  this.checklists = checklists;
97
+ this.results = results;
73
98
  }
74
99
  }
75
100
  exports.ReviewJson = ReviewJson;
101
+ // A checklist's resolved outcome, shared by review.json enforcement and the dashboard so both agree.
102
+ exports.CK_PASS = 'pass'; // review-<id>.json success:true
103
+ exports.CK_OVERRIDDEN = 'overridden'; // review-<id>.json success:false + non-empty override → 🟡
104
+ exports.CK_FAIL = 'fail'; // review-<id>.json success:false + no override → refuse
105
+ exports.CK_MISSING = 'missing'; // no verdict file and no inline ack → refuse (BLOCK)
106
+ exports.CK_ACKED = 'acknowledged'; // legacy inline checklists[] ack satisfied (back-compat)
107
+ class ChecklistVerdict {
108
+ id;
109
+ status; // one of CK_PASS | CK_OVERRIDDEN | CK_FAIL | CK_MISSING | CK_ACKED
110
+ detail; // reviewer output / override justification / ack notes (for the dashboard + errors)
111
+ constructor(id, status, detail) {
112
+ this.id = id;
113
+ this.status = status;
114
+ this.detail = detail;
115
+ }
116
+ }
117
+ exports.ChecklistVerdict = ChecklistVerdict;
76
118
  const RISK_LEVELS = ['green', 'yellow', 'red'];
77
119
  const EMOJI_FOR_LEVEL = { green: '🟢', yellow: '🟡', red: '🔴' };
78
120
  /** Locates + loads/validates the AI-authored review.json. `@injectable(bindingScopeValues.Singleton)` so it's drawn in the design. */
@@ -91,9 +133,6 @@ let ReviewJsonService = class ReviewJsonService {
91
133
  // in the JSON shape PLUS an instruction block naming the docs to read — diff-derived instructions
92
134
  // injected at exactly the moment the AI writes review.json.
93
135
  reviewJsonSchemaHint(filePath, required = []) {
94
- const checklistLine = required.length > 0
95
- ? `,\n "checklists": [{ "id": "<id from the list below>", "acknowledged": true, "notes": ["what you checked"] }]\n`
96
- : `\n`;
97
136
  return (`Write your PR review to:\n ${filePath}\n\n` +
98
137
  `with this exact JSON shape (riskEmoji optional — derived from riskLevel):\n\n` +
99
138
  `{\n` +
@@ -103,26 +142,36 @@ let ReviewJsonService = class ReviewJsonService {
103
142
  ` "summary": "5–10 sentence review summary",\n` +
104
143
  ` "violations": ["pattern/architecture violations you found (empty array if none)"],\n` +
105
144
  ` "risks": ["notable risks (empty array if none)"],\n` +
106
- ` "filesToReview": ["paths a human should look at (empty array if none)"]` +
107
- checklistLine +
145
+ ` "filesToReview": ["paths a human should look at (empty array if none)"]\n` +
108
146
  `}` +
109
- this.requiredChecklistHint(required));
147
+ this.requiredChecklistHint(filePath, required));
148
+ }
149
+ // The per-checklist review file path that sits beside review.json: review-<id>.json.
150
+ checklistResultPath(reviewJsonFilePath, checklistId) {
151
+ return path.join(path.dirname(reviewJsonFilePath), `review-${checklistId}.json`);
110
152
  }
111
- // The diff-triggered instruction block, appended ONLY when the branch triggered a checklist. This
112
- // is the consumer's review process, re-injected: read doc Y because the diff touched X. BLOCK
113
- // entries must be acknowledged in `checklists[]` or wp-finish-upsert-pr refuses to open the PR.
114
- requiredChecklistHint(required) {
153
+ // The diff-triggered instruction block, appended ONLY when the branch triggered a checklist. This is
154
+ // the consumer's review process, re-injected: read doc Y because the diff touched X, then write ONE
155
+ // file per checklist `review-<id>.json` so concurrent reviewer subagents never clobber a shared
156
+ // file. A BLOCK checklist with success:false and no override refuses to open the PR.
157
+ requiredChecklistHint(reviewJsonFilePath, required) {
115
158
  if (required.length === 0)
116
159
  return '';
117
- const lines = ['', '', 'This branch triggered company review checklist(s). BEFORE writing review.json, READ each'];
118
- lines.push('doc, walk its items against your diff, then add a `checklists[]` entry acknowledging it:');
160
+ const lines = ['', '', 'This branch triggered company review checklist(s). For EACH one below: READ its docs, walk'];
161
+ lines.push('the items against your diff, then write a SEPARATE file `review-<id>.json` beside review.json');
162
+ lines.push('with this shape (override optional — a free-text ship-anyway justification):');
163
+ lines.push('');
164
+ lines.push(' { "success": true, "output": "what you checked / what you found", "override": "" }');
119
165
  lines.push('');
120
166
  for (const req of required) {
121
167
  const gate = req.severity === checklist_config_1.CHECKLIST_BLOCK
122
- ? 'BLOCK — the PR will NOT open until you acknowledge it'
123
- : 'WARN — acknowledge if it applies (never blocks)';
168
+ ? 'BLOCK — success:false with no "override" will NOT open the PR'
169
+ : 'WARN — recorded on the dashboard; never blocks';
124
170
  lines.push(` • [${req.id}] ${req.title} (${gate})`);
171
+ lines.push(` write: ${this.checklistResultPath(reviewJsonFilePath, req.id)}`);
125
172
  lines.push(` docs to read: ${req.docs.join(', ')}`);
173
+ if (req.subagent.trim() !== '')
174
+ lines.push(` must be reviewed by the "${req.subagent.trim()}" subagent (its independent run is verified from the harness).`);
126
175
  if (req.blockMessage.trim() !== '')
127
176
  lines.push(` ${req.blockMessage.trim()}`);
128
177
  if (req.matchedFiles.length > 0)
@@ -146,7 +195,7 @@ let ReviewJsonService = class ReviewJsonService {
146
195
  throw new inform_ai_error_1.InformAiError(`Required review.json not found.\n\n${this.reviewJsonSchemaHint(filePath, required)}\n\n` +
147
196
  `Then re-run: pnpm wp-finish-upsert-pr`);
148
197
  }
149
- const raw = this.parseReviewJson(fs.readFileSync(filePath, 'utf8'), filePath);
198
+ const raw = this.parseReviewJson(fs.readFileSync(filePath, 'utf8'), filePath, required);
150
199
  if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {
151
200
  throw new inform_ai_error_1.InformAiError(`review.json must be a JSON object.\n\n${this.reviewJsonSchemaHint(filePath, required)}`);
152
201
  }
@@ -164,7 +213,8 @@ let ReviewJsonService = class ReviewJsonService {
164
213
  errors.push('"title" must be a non-empty, imperative PR title describing the change (no branch names).');
165
214
  }
166
215
  const acks = this.parseChecklistAcks(raw['checklists']);
167
- for (const err of this.requiredChecklistErrors(required, acks))
216
+ const results = this.loadChecklistResults(filePath, required);
217
+ for (const err of this.requiredChecklistErrors(required, acks, results))
168
218
  errors.push(err);
169
219
  if (errors.length > 0) {
170
220
  throw new inform_ai_error_1.InformAiError(`review.json has ${errors.length} error(s) — fix ALL, then re-run pnpm wp-finish-upsert-pr:\n\n` +
@@ -176,7 +226,7 @@ let ReviewJsonService = class ReviewJsonService {
176
226
  ? raw['riskEmoji']
177
227
  : (EMOJI_FOR_LEVEL[level] ?? '🟡');
178
228
  const summary = typeof raw['summary'] === 'string' ? raw['summary'] : '';
179
- return new ReviewJson(title, riskScore, level, emoji, summary, this.asStringArray(raw['violations']), this.asStringArray(raw['risks']), this.asStringArray(raw['filesToReview']), acks);
229
+ return new ReviewJson(title, riskScore, level, emoji, summary, this.asStringArray(raw['violations']), this.asStringArray(raw['risks']), this.asStringArray(raw['filesToReview']), acks, results);
180
230
  }
181
231
  // Parse the AI-authored `checklists[]` into typed ChecklistAck[]. Tolerant of a missing/garbage
182
232
  // field (→ []) and of non-object entries (skipped) — malformed acks simply fail to satisfy a BLOCK
@@ -198,23 +248,87 @@ let ReviewJsonService = class ReviewJsonService {
198
248
  }
199
249
  return acks;
200
250
  }
201
- // BLOCK requirements that are not acknowledged one error each, using the consumer's blockMessage
202
- // verbatim so the consumer owns the wording and webpieces owns the mechanism.
203
- requiredChecklistErrors(required, acks) {
251
+ // Read the per-checklist verdict files `review-<id>.json` that sit beside review.json one per
252
+ // triggered checklist. Tolerant like parseChecklistAcks: a missing file is simply absent from the
253
+ // result ( falls back to the inline ack / counts as unmet for a BLOCK), and a malformed one is
254
+ // skipped (an unknown/stale review-<id>.json never wedges the branch).
255
+ loadChecklistResults(reviewJsonFilePath, required) {
256
+ const results = [];
257
+ for (const req of required) {
258
+ const p = this.checklistResultPath(reviewJsonFilePath, req.id);
259
+ if (!fs.existsSync(p))
260
+ continue;
261
+ const parsed = this.parseChecklistResult(p, req.id);
262
+ if (parsed)
263
+ results.push(parsed);
264
+ }
265
+ return results;
266
+ }
267
+ // Resolve ONE checklist's verdict from its per-file result (preferred) or its legacy inline ack.
268
+ // Central so review.json enforcement AND the finish-command dashboard agree on the outcome.
269
+ resolveVerdict(req, acks, results) {
270
+ const result = results.find((r) => r.id === req.id);
271
+ if (result) {
272
+ if (result.success)
273
+ return new ChecklistVerdict(req.id, exports.CK_PASS, result.output);
274
+ if (result.override.trim() !== '')
275
+ return new ChecklistVerdict(req.id, exports.CK_OVERRIDDEN, result.override.trim());
276
+ return new ChecklistVerdict(req.id, exports.CK_FAIL, result.output);
277
+ }
278
+ const ack = acks.find((a) => a.id === req.id);
279
+ if (ack && ack.acknowledged)
280
+ return new ChecklistVerdict(req.id, exports.CK_ACKED, ack.notes.join('; '));
281
+ return new ChecklistVerdict(req.id, exports.CK_MISSING, '');
282
+ }
283
+ // BLOCK requirements whose verdict is FAIL (reviewed, found a problem, no override) or MISSING (no
284
+ // verdict written at all) → one error each. WARN never blocks. Uses the consumer's blockMessage /
285
+ // the reviewer's `output` verbatim so the consumer owns the wording and webpieces owns the mechanism.
286
+ requiredChecklistErrors(required, acks, results) {
204
287
  const errors = [];
205
288
  for (const req of required) {
206
289
  if (req.severity !== checklist_config_1.CHECKLIST_BLOCK)
207
290
  continue;
208
- const ack = acks.find((a) => a.id === req.id);
209
- if (!ack || !ack.acknowledged) {
291
+ const verdict = this.resolveVerdict(req, acks, results);
292
+ if (verdict.status === exports.CK_FAIL) {
293
+ const msg = req.blockMessage.trim() !== '' ? `${req.blockMessage.trim()} ` : '';
294
+ errors.push(`Checklist "${req.id}" (${req.title}) FAILED review. ${msg}` +
295
+ `The reviewer wrote:\n ${verdict.detail.split('\n').join('\n ')}\n` +
296
+ ` Fix it, then re-run; or set a non-empty "override" in ${this.checklistFileName(req.id)} to ship anyway with a stated justification.`);
297
+ }
298
+ else if (verdict.status === exports.CK_MISSING) {
210
299
  const docs = req.docs.length > 0 ? ` Read: ${req.docs.join(', ')}.` : '';
211
300
  const msg = req.blockMessage.trim() !== '' ? `${req.blockMessage.trim()} ` : '';
212
- errors.push(`Checklist "${req.id}" (${req.title}) is REQUIRED for this diff but not acknowledged. ${msg}` +
213
- `Add {"id":"${req.id}","acknowledged":true,"notes":[...]} to "checklists" once you have walked it.${docs}`);
301
+ errors.push(`Checklist "${req.id}" (${req.title}) is REQUIRED for this diff but has no verdict. ${msg}` +
302
+ `Write ${this.checklistFileName(req.id)} with {"success":true,"output":""} once you have walked it.${docs}`);
214
303
  }
215
304
  }
216
305
  return errors;
217
306
  }
307
+ checklistFileName(checklistId) {
308
+ return `review-${checklistId}.json`;
309
+ }
310
+ // Parse one review-<id>.json into a ChecklistResult, or null when malformed. Tolerant: missing
311
+ // `success` counts as false (fail-closed), `output`/`override` default to ''.
312
+ // webpieces-disable no-any-unknown -- opaque parsed JSON, narrowed field-by-field
313
+ parseChecklistResult(filePath, id) {
314
+ // webpieces-disable no-unmanaged-exceptions -- chokepoint: a malformed per-checklist file is skipped, not fatal
315
+ // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
316
+ try {
317
+ // webpieces-disable no-any-unknown -- parsed JSON is opaque until narrowed below
318
+ const raw = JSON.parse(fs.readFileSync(filePath, 'utf8'));
319
+ if (typeof raw !== 'object' || raw === null || Array.isArray(raw))
320
+ return null;
321
+ const success = raw['success'] === true;
322
+ const output = typeof raw['output'] === 'string' ? raw['output'] : '';
323
+ const override = typeof raw['override'] === 'string' ? raw['override'] : '';
324
+ return new ChecklistResult(id, success, output, override);
325
+ }
326
+ catch (err) {
327
+ const error = (0, to_error_1.toError)(err);
328
+ void error;
329
+ return null;
330
+ }
331
+ }
218
332
  // webpieces-disable no-any-unknown -- opaque parsed JSON value, narrowed to string[] here
219
333
  asStringArray(value) {
220
334
  if (!Array.isArray(value))
@@ -222,9 +336,11 @@ let ReviewJsonService = class ReviewJsonService {
222
336
  // webpieces-disable no-any-unknown -- element of an opaque JSON array, narrowed by the type guard
223
337
  return value.filter((v) => typeof v === 'string');
224
338
  }
225
- // Parse opaque AI-authored JSON, converting a SyntaxError into a readable InformAiError.
339
+ // Parse opaque AI-authored JSON, converting a SyntaxError into a readable InformAiError. `required`
340
+ // is threaded through so a JSON syntax error still prints the checklist instructions the AI needs —
341
+ // exactly when it most needs them — rather than the bare schema.
226
342
  // webpieces-disable no-any-unknown -- returns the opaque parsed object; loadReviewJson narrows each field
227
- parseReviewJson(raw, filePath) {
343
+ parseReviewJson(raw, filePath, required) {
228
344
  // webpieces-disable no-unmanaged-exceptions -- chokepoint: convert JSON.parse SyntaxError to an InformAiError for the AI
229
345
  // eslint-disable-next-line @webpieces/no-unmanaged-exceptions
230
346
  try {
@@ -233,7 +349,7 @@ let ReviewJsonService = class ReviewJsonService {
233
349
  }
234
350
  catch (err) {
235
351
  const error = (0, to_error_1.toError)(err);
236
- throw new inform_ai_error_1.InformAiError(`review.json is not valid JSON (${error.message}).\n\n${this.reviewJsonSchemaHint(filePath)}\n\n` +
352
+ throw new inform_ai_error_1.InformAiError(`review.json is not valid JSON (${error.message}).\n\n${this.reviewJsonSchemaHint(filePath, required)}\n\n` +
237
353
  `Then re-run: pnpm wp-finish-upsert-pr`);
238
354
  }
239
355
  }