@ai-sdlc/orchestrator 0.9.0 → 0.10.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/__test-helpers/git-env.d.ts +40 -0
- package/dist/__test-helpers/git-env.js +73 -0
- package/dist/admission-composite.d.ts +22 -0
- package/dist/admission-composite.js +85 -2
- package/dist/admission-enrichment.js +12 -2
- package/dist/admission-hc.d.ts +20 -1
- package/dist/admission-hc.js +12 -1
- package/dist/admission-score.d.ts +14 -0
- package/dist/admission-score.js +7 -1
- package/dist/cli/commands/git-remote.d.ts +25 -0
- package/dist/cli/commands/git-remote.js +80 -1
- package/dist/cli/commands/init-features.d.ts +208 -0
- package/dist/cli/commands/init-features.js +473 -0
- package/dist/cli/commands/init-templates.d.ts +104 -0
- package/dist/cli/commands/init-templates.js +399 -0
- package/dist/cli/commands/init.d.ts +22 -0
- package/dist/cli/commands/init.js +158 -1
- package/dist/design-authority.d.ts +13 -0
- package/dist/design-authority.js +13 -0
- package/dist/execute.js +8 -5
- package/dist/models/classifier.d.ts +3 -1
- package/dist/models/classifier.js +61 -5
- package/dist/pillar-breakdown.d.ts +23 -1
- package/dist/pillar-breakdown.js +9 -0
- package/dist/runtime/attestations.d.ts +502 -10
- package/dist/runtime/attestations.js +759 -24
- package/dist/runtime/parallelism-flag.d.ts +21 -3
- package/dist/runtime/parallelism-flag.js +27 -6
- package/dist/runtime/port-allocator.d.ts +11 -0
- package/dist/runtime/port-allocator.js +30 -0
- package/package.json +3 -2
|
@@ -12,6 +12,20 @@
|
|
|
12
12
|
* orchestrator version that ran init, with an inline opt-out hint.
|
|
13
13
|
* - Skip `.cursor/mcp.json` unless Cursor is detected (project-local
|
|
14
14
|
* `.cursor/`, user-global `~/.cursor/`) or `--cursor` is passed.
|
|
15
|
+
*
|
|
16
|
+
* AISDLC-143 enhancements (Q4(b) of the quality-gate redesign):
|
|
17
|
+
* - Default invocation is now an interactive WIZARD (DoR / attestation /
|
|
18
|
+
* classifier / branch-protection prompts) so adopters get a guided
|
|
19
|
+
* bootstrap instead of having to read the docs to discover features.
|
|
20
|
+
* - `--yes` short-circuits all prompts to "accept defaults" (CI/scripts).
|
|
21
|
+
* - `--with-X` flags opt into individual features without prompting.
|
|
22
|
+
* - `--add <feature>` extends an already-initialized repo with a single
|
|
23
|
+
* feature, idempotently — re-running on an initialized repo is safe.
|
|
24
|
+
* - `.github/workflows/ai-sdlc-gate.yml` is scaffolded UNCONDITIONALLY
|
|
25
|
+
* so every adopter gets the `ai-sdlc/pr-ready` rollup check on day one
|
|
26
|
+
* (Q1: prescriptive default).
|
|
27
|
+
* - A "next steps" summary closes the run with operator action items
|
|
28
|
+
* conditional on which features were chosen.
|
|
15
29
|
*/
|
|
16
30
|
import { Command } from 'commander';
|
|
17
31
|
import { existsSync, mkdirSync, writeFileSync, readFileSync, appendFileSync } from 'node:fs';
|
|
@@ -20,7 +34,8 @@ import { detectAgentsDetailed, installMcpServer } from './mcp-setup.js';
|
|
|
20
34
|
import { detectWorkspace, generateWorkspaceYaml } from './workspace-detect.js';
|
|
21
35
|
import { detectGitRemote, applyRemoteToPipelineYaml } from './git-remote.js';
|
|
22
36
|
import { resolveVersions, formatVersionBlock } from '../versions.js';
|
|
23
|
-
|
|
37
|
+
import { applyFeatureSelection, buildProductionAdapters, ensureClaudeMdPointer, renderNextSteps, resolveFeatureSelection, } from './init-features.js';
|
|
38
|
+
export const PIPELINE_YAML = `apiVersion: ai-sdlc.io/v1alpha1
|
|
24
39
|
kind: Pipeline
|
|
25
40
|
metadata:
|
|
26
41
|
name: default
|
|
@@ -48,6 +63,23 @@ spec:
|
|
|
48
63
|
- name: review
|
|
49
64
|
qualityGates:
|
|
50
65
|
- default-gates
|
|
66
|
+
# backlog: holds settings specific to the backlog-task (/ai-sdlc execute)
|
|
67
|
+
# workflow. These were formerly in a separate pipeline-backlog.yaml file
|
|
68
|
+
# (deprecated by AISDLC-245.5). Slash commands + pipeline-cli readers
|
|
69
|
+
# prefer this canonical location.
|
|
70
|
+
backlog:
|
|
71
|
+
branching:
|
|
72
|
+
pattern: 'ai-sdlc/{issueIdLower}-{slug}'
|
|
73
|
+
targetBranch: main
|
|
74
|
+
cleanup: on-merge
|
|
75
|
+
pullRequest:
|
|
76
|
+
titleTemplate: 'feat: {issueTitle} ({issueId})'
|
|
77
|
+
descriptionSections:
|
|
78
|
+
- summary
|
|
79
|
+
- changes
|
|
80
|
+
- closes
|
|
81
|
+
includeProvenance: true
|
|
82
|
+
closeKeyword: References
|
|
51
83
|
`;
|
|
52
84
|
export const AGENT_ROLE_TIERS = ['coding', 'research', 'meta'];
|
|
53
85
|
const AGENT_ROLE_YAML_CODING = `apiVersion: ai-sdlc.io/v1alpha1
|
|
@@ -301,6 +333,45 @@ function initWorkspaceRoot(workspacePath, repos, configDirName, dryRun) {
|
|
|
301
333
|
console.log(` created workspace.yaml`);
|
|
302
334
|
}
|
|
303
335
|
}
|
|
336
|
+
/**
|
|
337
|
+
* Build the WizardFlags struct from Commander's parsed options. Pulled
|
|
338
|
+
* out into a function so tests can drive the wizard pipeline directly
|
|
339
|
+
* without going through Commander's stateful option store.
|
|
340
|
+
*/
|
|
341
|
+
export function buildWizardFlags(opts) {
|
|
342
|
+
const addRaw = typeof opts.add === 'string' ? opts.add : undefined;
|
|
343
|
+
let add;
|
|
344
|
+
if (addRaw === 'dor' ||
|
|
345
|
+
addRaw === 'attestation' ||
|
|
346
|
+
addRaw === 'classifier' ||
|
|
347
|
+
addRaw === 'branch-protection') {
|
|
348
|
+
add = addRaw;
|
|
349
|
+
}
|
|
350
|
+
return {
|
|
351
|
+
yes: !!opts.yes,
|
|
352
|
+
withDor: !!opts.withDor,
|
|
353
|
+
withAttestation: !!opts.withAttestation,
|
|
354
|
+
withClassifier: !!opts.withClassifier,
|
|
355
|
+
withBranchProtection: !!opts.withBranchProtection,
|
|
356
|
+
add,
|
|
357
|
+
dryRun: !!opts.dryRun,
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
/** Validate a `--add` arg and return either the normalized value or null+error. */
|
|
361
|
+
function validateAddArg(addRaw) {
|
|
362
|
+
if (addRaw === undefined || addRaw === null || addRaw === false)
|
|
363
|
+
return { ok: true };
|
|
364
|
+
if (typeof addRaw !== 'string')
|
|
365
|
+
return { ok: false, error: `--add must be a string` };
|
|
366
|
+
const allowed = ['dor', 'attestation', 'classifier', 'branch-protection'];
|
|
367
|
+
if (!allowed.includes(addRaw)) {
|
|
368
|
+
return {
|
|
369
|
+
ok: false,
|
|
370
|
+
error: `--add: unknown feature '${addRaw}'. Expected one of: ${allowed.join(', ')}.`,
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
return { ok: true, value: addRaw };
|
|
374
|
+
}
|
|
304
375
|
export const initCommand = new Command('init')
|
|
305
376
|
.description('Initialize AI-SDLC configuration in the current project')
|
|
306
377
|
.option('--dry-run', 'Show what would be created without writing files')
|
|
@@ -308,6 +379,13 @@ export const initCommand = new Command('init')
|
|
|
308
379
|
.option('--cursor', 'Force-install Cursor MCP config even if Cursor is not detected')
|
|
309
380
|
.option('-d, --dir <path>', 'Config directory name', '.ai-sdlc')
|
|
310
381
|
.option('--role <tier>', `Agent-role tool tier: ${AGENT_ROLE_TIERS.join(' | ')} (default: coding)`, 'coding')
|
|
382
|
+
// ── AISDLC-143 wizard flags ─────────────────────────────────────────
|
|
383
|
+
.option('-y, --yes', 'Accept all defaults (non-interactive; CI/scripts)')
|
|
384
|
+
.option('--with-dor', 'Scaffold Definition-of-Ready gate config + workflow')
|
|
385
|
+
.option('--with-attestation', 'Scaffold attestation infrastructure (audit-only)')
|
|
386
|
+
.option('--with-classifier', 'Scaffold review classifier config stub')
|
|
387
|
+
.option('--with-branch-protection', 'Apply recommended branch-protection rule to main (requires gh)')
|
|
388
|
+
.option('--add <feature>', 'Extend an already-initialized repo with a single feature: dor | attestation | classifier | branch-protection')
|
|
311
389
|
.action(async (opts) => {
|
|
312
390
|
const projectDir = process.cwd();
|
|
313
391
|
const configDirName = opts.dir ?? '.ai-sdlc';
|
|
@@ -320,6 +398,41 @@ export const initCommand = new Command('init')
|
|
|
320
398
|
}
|
|
321
399
|
const tier = tierInput;
|
|
322
400
|
const cursorOptIn = !!opts.cursor;
|
|
401
|
+
// ── Validate --add early so we error before doing any work. ────────
|
|
402
|
+
const addCheck = validateAddArg(opts.add);
|
|
403
|
+
if (!addCheck.ok) {
|
|
404
|
+
console.error(`Error: ${addCheck.error}`);
|
|
405
|
+
process.exitCode = 1;
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
const flags = buildWizardFlags(opts);
|
|
409
|
+
// ── --add path: extend an already-initialized repo ────────────────
|
|
410
|
+
// AC #7: skip the "always-scaffold-baseline" path entirely; the
|
|
411
|
+
// wizard dispatcher's `--add` branch knows to write only the chosen
|
|
412
|
+
// feature's templates (no pipeline.yaml, no MCP setup, no workspace
|
|
413
|
+
// detection). This is the safe re-run path on a repo that already
|
|
414
|
+
// ran `ai-sdlc init` once.
|
|
415
|
+
if (flags.add) {
|
|
416
|
+
const adapters = buildProductionAdapters();
|
|
417
|
+
const selection = await resolveFeatureSelection(flags, adapters);
|
|
418
|
+
console.log(`Extending AI-SDLC config with --add ${flags.add}:`);
|
|
419
|
+
console.log('');
|
|
420
|
+
const result = await applyFeatureSelection(projectDir, selection, flags, adapters);
|
|
421
|
+
renderNextSteps(selection, result, adapters);
|
|
422
|
+
// Reviewer feedback (round 2, suggestion #5): when the operator
|
|
423
|
+
// explicitly requested branch-protection via a non-interactive flag
|
|
424
|
+
// (--add branch-protection here; --yes / --with-branch-protection
|
|
425
|
+
// in runWizardStage) and the apply failed (gh missing, not
|
|
426
|
+
// authenticated, etc.), surface a non-zero exit so CI scripts can
|
|
427
|
+
// detect failure instead of seeing the silent log line.
|
|
428
|
+
if (flags.add === 'branch-protection' &&
|
|
429
|
+
result.branchProtection &&
|
|
430
|
+
!result.branchProtection.applied &&
|
|
431
|
+
result.branchProtection.error) {
|
|
432
|
+
process.exitCode = 1;
|
|
433
|
+
}
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
323
436
|
// ── version provenance (AC #1) ────────────────────────────────────
|
|
324
437
|
const versions = resolveVersions({ workDir: projectDir });
|
|
325
438
|
console.log(formatVersionBlock(versions));
|
|
@@ -377,6 +490,12 @@ export const initCommand = new Command('init')
|
|
|
377
490
|
}
|
|
378
491
|
}
|
|
379
492
|
console.log(`\nAI-SDLC workspace initialized in ${projectDir}/`);
|
|
493
|
+
// ── AISDLC-143 wizard (workspace root) ────────────────────────
|
|
494
|
+
// The wizard runs ONCE at the workspace root: the baseline gate
|
|
495
|
+
// workflow + per-feature workflows live at `<workspace-root>/.github/`,
|
|
496
|
+
// not in each child repo. Children share the same CI from the
|
|
497
|
+
// root since GHA workflows always live at the repo root anyway.
|
|
498
|
+
await runWizardStage(projectDir, flags);
|
|
380
499
|
console.log(`Run 'ai-sdlc health' to verify your configuration.`);
|
|
381
500
|
}
|
|
382
501
|
else {
|
|
@@ -399,7 +518,45 @@ export const initCommand = new Command('init')
|
|
|
399
518
|
}
|
|
400
519
|
}
|
|
401
520
|
console.log(`\nAI-SDLC config initialized in ${join(projectDir, configDirName)}/`);
|
|
521
|
+
// ── AISDLC-143 wizard (single-repo) ──────────────────────────
|
|
522
|
+
await runWizardStage(projectDir, flags);
|
|
402
523
|
console.log(`Run 'ai-sdlc health' to verify your configuration.`);
|
|
403
524
|
}
|
|
404
525
|
});
|
|
526
|
+
/**
|
|
527
|
+
* Run the AISDLC-143 wizard stage: prompt the user (or short-circuit on
|
|
528
|
+
* --yes / --with-X), apply the chosen feature templates, append the
|
|
529
|
+
* CLAUDE.md pointer, and render the "next steps" summary.
|
|
530
|
+
*
|
|
531
|
+
* Pulled out of the inline action body so both the single-repo and
|
|
532
|
+
* workspace-root branches share the same wiring. Adapters are built
|
|
533
|
+
* once here (production = real disk writes; tests inject stubs by
|
|
534
|
+
* calling `applyFeatureSelection`/`renderNextSteps` directly).
|
|
535
|
+
*/
|
|
536
|
+
async function runWizardStage(projectDir, flags) {
|
|
537
|
+
const adapters = buildProductionAdapters();
|
|
538
|
+
console.log('');
|
|
539
|
+
console.log('━━━ Feature wizard ━━━');
|
|
540
|
+
console.log('');
|
|
541
|
+
const selection = await resolveFeatureSelection(flags, adapters);
|
|
542
|
+
console.log('');
|
|
543
|
+
console.log('Scaffolding selected features:');
|
|
544
|
+
const result = await applyFeatureSelection(projectDir, selection, flags, adapters);
|
|
545
|
+
ensureClaudeMdPointer(projectDir, adapters, flags.dryRun);
|
|
546
|
+
renderNextSteps(selection, result, adapters);
|
|
547
|
+
// Reviewer feedback (round 2, suggestion #5): when branch-protection
|
|
548
|
+
// was non-interactively requested (--with-branch-protection or --yes,
|
|
549
|
+
// both used by CI scripts) and the apply failed (gh missing, not
|
|
550
|
+
// authenticated, etc.), surface a non-zero exit so CI can detect
|
|
551
|
+
// failure instead of seeing the silent log line. Interactive prompt
|
|
552
|
+
// answers don't trip this — the human already saw the error and can
|
|
553
|
+
// re-run `ai-sdlc init --add branch-protection` themselves.
|
|
554
|
+
const branchProtectionRequestedNonInteractively = flags.yes || flags.withBranchProtection;
|
|
555
|
+
if (branchProtectionRequestedNonInteractively &&
|
|
556
|
+
result.branchProtection &&
|
|
557
|
+
!result.branchProtection.applied &&
|
|
558
|
+
result.branchProtection.error) {
|
|
559
|
+
process.exitCode = 1;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
405
562
|
//# sourceMappingURL=init.js.map
|
|
@@ -5,6 +5,19 @@
|
|
|
5
5
|
* `designAuthority` principal on the resolved DesignSystemBinding, and
|
|
6
6
|
* parses the explicit signal type from issue labels (preferred) or
|
|
7
7
|
* structured comment markers.
|
|
8
|
+
*
|
|
9
|
+
* AISDLC-171 / RFC-0009 §13 OQ-8 anchor:
|
|
10
|
+
* HC_design fires ONLY when one of the issue's participants (author or
|
|
11
|
+
* commenter) is a principal listed in
|
|
12
|
+
* `DesignSystemBinding.spec.stewardship.designAuthority.principals`.
|
|
13
|
+
* Per RFC-0008 §14.2, non-principal participants' design opinions are
|
|
14
|
+
* routed through HC_consensus (not HC_design) — this prevents anyone
|
|
15
|
+
* from emitting full-weight HC_design signals just by labeling an
|
|
16
|
+
* issue. To distinguish "no DSB at all" from "DSB exists but no
|
|
17
|
+
* principal participated" at the breakdown surface, see
|
|
18
|
+
* `principalsDeclared` on `DesignAuthoritySignal` (set by
|
|
19
|
+
* `buildDesignAuthoritySignal` in `admission-enrichment.ts`) and
|
|
20
|
+
* `pillarBreakdown.shared.hcComposite.designAuthorityConfigured`.
|
|
8
21
|
*/
|
|
9
22
|
import type { DesignSystemBinding } from '@ai-sdlc/reference';
|
|
10
23
|
import type { DesignAuthoritySignalType } from './admission-score.js';
|
package/dist/design-authority.js
CHANGED
|
@@ -5,6 +5,19 @@
|
|
|
5
5
|
* `designAuthority` principal on the resolved DesignSystemBinding, and
|
|
6
6
|
* parses the explicit signal type from issue labels (preferred) or
|
|
7
7
|
* structured comment markers.
|
|
8
|
+
*
|
|
9
|
+
* AISDLC-171 / RFC-0009 §13 OQ-8 anchor:
|
|
10
|
+
* HC_design fires ONLY when one of the issue's participants (author or
|
|
11
|
+
* commenter) is a principal listed in
|
|
12
|
+
* `DesignSystemBinding.spec.stewardship.designAuthority.principals`.
|
|
13
|
+
* Per RFC-0008 §14.2, non-principal participants' design opinions are
|
|
14
|
+
* routed through HC_consensus (not HC_design) — this prevents anyone
|
|
15
|
+
* from emitting full-weight HC_design signals just by labeling an
|
|
16
|
+
* issue. To distinguish "no DSB at all" from "DSB exists but no
|
|
17
|
+
* principal participated" at the breakdown surface, see
|
|
18
|
+
* `principalsDeclared` on `DesignAuthoritySignal` (set by
|
|
19
|
+
* `buildDesignAuthoritySignal` in `admission-enrichment.ts`) and
|
|
20
|
+
* `pillarBreakdown.shared.hcComposite.designAuthorityConfigured`.
|
|
8
21
|
*/
|
|
9
22
|
/**
|
|
10
23
|
* Label-encoded design signal types. Short, stable slugs keep GitHub
|
package/dist/execute.js
CHANGED
|
@@ -45,11 +45,14 @@ export async function executePipeline(issueId, options = {}) {
|
|
|
45
45
|
(options.useStructuredLogger ? createStructuredConsoleLogger() : createLogger());
|
|
46
46
|
const auditLog = options.auditLog ?? createDefaultAuditLog(workDir);
|
|
47
47
|
const metricStore = options.metricStore;
|
|
48
|
-
// RFC-0010 §6/§7 Phase 2: parallelism
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
//
|
|
48
|
+
// RFC-0010 §6/§7 Phase 2: parallelism. Per AISDLC-116 (maintainer directive 2026-05-01),
|
|
49
|
+
// AI_SDLC_PARALLELISM now defaults to 'on' — corpus-driven (no parallelism-related incidents
|
|
50
|
+
// in the trailing observation window) rather than calendar-driven. Explicit
|
|
51
|
+
// 'experimental' is preserved for callers pinning the pre-promotion mode; explicit
|
|
52
|
+
// 'off' / 'disabled' / 'false' / '0' is the opt-out path. When the resolved mode is
|
|
53
|
+
// not 'off', a WorktreePoolManager is instantiated for the worker-pool dispatcher
|
|
54
|
+
// (RFC-0010 §9). The manager is constructed but not yet routed through — Phase 2
|
|
55
|
+
// shipped the wire-in surface; Phase 3 wires the worker pool to consume it.
|
|
53
56
|
const parallelismMode = readParallelismMode();
|
|
54
57
|
let worktreePool;
|
|
55
58
|
if (parallelismMode !== 'off') {
|
|
@@ -51,7 +51,9 @@ export interface DiffSummary {
|
|
|
51
51
|
* Apply the default classifier ruleset from RFC §12.3 to a diff summary. Used as the
|
|
52
52
|
* fallback when no LLM classifier is configured, and as the seed prompt for LLM-based
|
|
53
53
|
* classifiers. Always returns confident: true with confidence: 1.0 because these are
|
|
54
|
-
* deterministic rules.
|
|
54
|
+
* deterministic rules. AISDLC-145 added the docs denylist + widened
|
|
55
|
+
* auth/lockfile/CI predicates to close downgrade vectors flagged by the
|
|
56
|
+
* AISDLC-141 security reviewer.
|
|
55
57
|
*/
|
|
56
58
|
export declare function defaultRulesetDecision(diff: DiffSummary): ClassifierOutput;
|
|
57
59
|
export interface CalibrationLogEntry {
|
|
@@ -146,11 +146,52 @@ export function validateClassifierOutput(value) {
|
|
|
146
146
|
}
|
|
147
147
|
return { ok: true, value: obj };
|
|
148
148
|
}
|
|
149
|
+
// ── AISDLC-145 path-classification helpers ─────────────────────────────────────────────
|
|
150
|
+
//
|
|
151
|
+
// These predicates are duplicated verbatim in the pipeline-cli copy
|
|
152
|
+
// (`pipeline-cli/src/classifier/classifier.ts`). Keep them in sync — drift
|
|
153
|
+
// here silently shifts which reviewers fire between callers. A future
|
|
154
|
+
// consolidation task should extract a single `@ai-sdlc/classifier-ruleset`
|
|
155
|
+
// package both sides import; tracked as a follow-up to AISDLC-145.
|
|
156
|
+
/** Renderable-docs / image extensions allowed in the docs-only branch. */
|
|
157
|
+
const DOCS_EXTENSIONS_RE = /\.(md|rst|txt|png|jpe?g|svg|gif|ico|pdf)$/i;
|
|
158
|
+
/**
|
|
159
|
+
* Filenames that look secret-y or executable-y and must NEVER be classified
|
|
160
|
+
* as docs even if they sit under `docs/`. Hits include `.env`, `.env.local`,
|
|
161
|
+
* `private-key.pem`, `signing.key`, `install.sh`, `Dockerfile`, `Dockerfile.prod`,
|
|
162
|
+
* `package-lock.json`, etc. Anchored on the basename so path-prefix doesn't
|
|
163
|
+
* matter.
|
|
164
|
+
*/
|
|
165
|
+
const DOCS_DENYLIST_RE = /(?:^|\/)(\.env(?:\..+)?|.+\.pem|.+\.key|.+\.sh|Dockerfile.*|.+\.lock)$/i;
|
|
166
|
+
/** True iff the path is safe to treat as documentation-only (no security review). */
|
|
167
|
+
function isDocsLikePath(p) {
|
|
168
|
+
if (DOCS_DENYLIST_RE.test(p))
|
|
169
|
+
return false;
|
|
170
|
+
return DOCS_EXTENSIONS_RE.test(p);
|
|
171
|
+
}
|
|
172
|
+
/** Auth-tier secret files (env vars, private keys) — treated as auth-touching. */
|
|
173
|
+
function isSecretFilePath(p) {
|
|
174
|
+
return /(?:^|\/)(\.env(?:\..+)?|.+\.pem|.+\.key)$/i.test(p);
|
|
175
|
+
}
|
|
176
|
+
/** Supply-chain lockfile detection (widened in AISDLC-145). */
|
|
177
|
+
function isLockfilePath(p) {
|
|
178
|
+
return /(?:^|\/)(package(-lock)?\.json|requirements\.txt|pnpm-lock\.yaml|yarn\.lock|Cargo\.lock|poetry\.lock|Pipfile\.lock|Gemfile\.lock|composer\.lock|go\.sum|bun\.lockb)$/i.test(p);
|
|
179
|
+
}
|
|
180
|
+
/** CI-config detection (widened beyond GitHub Actions in AISDLC-145). */
|
|
181
|
+
function isCiPath(p) {
|
|
182
|
+
if (p.startsWith('.github/workflows/'))
|
|
183
|
+
return true;
|
|
184
|
+
if (p.startsWith('.circleci/'))
|
|
185
|
+
return true;
|
|
186
|
+
return /(?:^|\/)(\.gitlab-ci\.yml|Jenkinsfile|azure-pipelines\.yml)$/i.test(p);
|
|
187
|
+
}
|
|
149
188
|
/**
|
|
150
189
|
* Apply the default classifier ruleset from RFC §12.3 to a diff summary. Used as the
|
|
151
190
|
* fallback when no LLM classifier is configured, and as the seed prompt for LLM-based
|
|
152
191
|
* classifiers. Always returns confident: true with confidence: 1.0 because these are
|
|
153
|
-
* deterministic rules.
|
|
192
|
+
* deterministic rules. AISDLC-145 added the docs denylist + widened
|
|
193
|
+
* auth/lockfile/CI predicates to close downgrade vectors flagged by the
|
|
194
|
+
* AISDLC-141 security reviewer.
|
|
154
195
|
*/
|
|
155
196
|
export function defaultRulesetDecision(diff) {
|
|
156
197
|
if (diff.filesChanged === 0) {
|
|
@@ -161,7 +202,15 @@ export function defaultRulesetDecision(diff) {
|
|
|
161
202
|
confidence: 1,
|
|
162
203
|
};
|
|
163
204
|
}
|
|
164
|
-
|
|
205
|
+
// AISDLC-145 hardening: the docs branch is a security DOWNGRADE — it skips
|
|
206
|
+
// both `testing` and `security` reviewers. So the predicate must be
|
|
207
|
+
// conservative: a docs-like file is one whose extension is in the safe set
|
|
208
|
+
// (renderable docs / images) AND is NOT on the unconditional denylist of
|
|
209
|
+
// executable-or-secret-looking filenames. Pre-145 the rule was just
|
|
210
|
+
// `p.startsWith('docs/')`, which let `docs/install.sh`, `docs/.env`,
|
|
211
|
+
// `docs/private-key.pem`, `docs/Dockerfile`, etc. silently bypass the
|
|
212
|
+
// security reviewer. See the AISDLC-141 reviewer findings.
|
|
213
|
+
const allDocs = diff.paths.every((p) => isDocsLikePath(p));
|
|
165
214
|
if (allDocs) {
|
|
166
215
|
return {
|
|
167
216
|
reviewers: ['critic'],
|
|
@@ -170,9 +219,16 @@ export function defaultRulesetDecision(diff) {
|
|
|
170
219
|
confidence: 0.95,
|
|
171
220
|
};
|
|
172
221
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
222
|
+
// AISDLC-145: widen auth/secret detection. The pre-145 regex
|
|
223
|
+
// `(auth|crypto|secrets?)` missed common identity/authn paths
|
|
224
|
+
// (`oauth/`, `iam/`, `jwt/`, `session/`, `login.ts`, `rbac/`, `tokens.ts`,
|
|
225
|
+
// `credentials.ts`, `password.ts`, `signin/`, `signup/`). Those still ran 3
|
|
226
|
+
// reviewers via the default branch but never got the opus model bump.
|
|
227
|
+
// Also: `.env*`, `*.pem`, `*.key` files are treated as auth-tier — they
|
|
228
|
+
// contain or directly grant credentials.
|
|
229
|
+
const touchesAuth = diff.paths.some((p) => /(?:^|\/)(auth|oauth|crypto|secrets?|iam|jwt|session|login|rbac|tokens?|credentials?|password|signin|signup)\b/i.test(p) || isSecretFilePath(p));
|
|
230
|
+
const touchesLockfiles = diff.paths.some((p) => isLockfilePath(p));
|
|
231
|
+
const touchesCi = diff.paths.some((p) => isCiPath(p));
|
|
176
232
|
if (touchesAuth) {
|
|
177
233
|
return {
|
|
178
234
|
reviewers: ['testing', 'critic', 'security'],
|
|
@@ -36,9 +36,31 @@ export interface SharedDimensions {
|
|
|
36
36
|
* populates this once the SA-1/SA-2/SA-3 decomposition lands.
|
|
37
37
|
*/
|
|
38
38
|
saAlpha3?: number;
|
|
39
|
-
/**
|
|
39
|
+
/**
|
|
40
|
+
* Per-channel HC breakdown with the tanh composite for reference.
|
|
41
|
+
*
|
|
42
|
+
* `designAuthorityConfigured` (AISDLC-171) — diagnostic flag set when
|
|
43
|
+
* the resolved DSB declares any `stewardship.designAuthority.principals`
|
|
44
|
+
* entries, regardless of whether one of them participated in the issue.
|
|
45
|
+
* Lets operators distinguish three pillarBreakdown states for the design
|
|
46
|
+
* channel:
|
|
47
|
+
* - `design = 0` and `configured === undefined` → no DSB resolved
|
|
48
|
+
* (preDesignSystem). HC_design intentionally inert.
|
|
49
|
+
* - `design = 0` and `configured === true` → DSB declares design
|
|
50
|
+
* authority but no principal participated as author/commenter.
|
|
51
|
+
* HC_design intentionally 0 per RFC-0008 §14.2 (only principals
|
|
52
|
+
* emit full-weight HC_design signals; non-principal opinions route
|
|
53
|
+
* through HC_consensus, not HC_design).
|
|
54
|
+
* - `design ≠ 0` and `configured === true` → a principal
|
|
55
|
+
* participated; signal weight reflects label-derived signalType.
|
|
56
|
+
*
|
|
57
|
+
* The `false` case (DSB exists but `principals` is empty) is also
|
|
58
|
+
* surfaced for completeness — a DSB without designAuthority principals
|
|
59
|
+
* cannot ever fire HC_design and operators should know.
|
|
60
|
+
*/
|
|
40
61
|
hcComposite: HcChannelBreakdown & {
|
|
41
62
|
value: number;
|
|
63
|
+
designAuthorityConfigured?: boolean;
|
|
42
64
|
};
|
|
43
65
|
}
|
|
44
66
|
export interface TensionFlag {
|
package/dist/pillar-breakdown.js
CHANGED
|
@@ -70,6 +70,15 @@ export function computePillarBreakdown(composite) {
|
|
|
70
70
|
decision: b.humanCurve.hcDecision,
|
|
71
71
|
design: b.humanCurve.hcDesign,
|
|
72
72
|
value: b.humanCurve.hcComposite,
|
|
73
|
+
// AISDLC-171: only include the flag when the underlying signal
|
|
74
|
+
// populated it (i.e., a DSB was resolved). Leaving it `undefined`
|
|
75
|
+
// when no DSB was supplied keeps the preDesignSystem state
|
|
76
|
+
// distinct from the "configured but inactive" state at the API
|
|
77
|
+
// surface — operators inspecting `pillarBreakdown.shared` can
|
|
78
|
+
// tell the three states apart without reading the DSB themselves.
|
|
79
|
+
...(b.humanCurve.designAuthorityConfigured !== undefined
|
|
80
|
+
? { designAuthorityConfigured: b.humanCurve.designAuthorityConfigured }
|
|
81
|
+
: {}),
|
|
73
82
|
},
|
|
74
83
|
};
|
|
75
84
|
const tensions = detectTensions({ product, design, engineering, shared, tensions: [] });
|