yam-flow 2.8.4 → 2.8.5

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/AGENTS.md CHANGED
@@ -24,6 +24,10 @@
24
24
  22. When an artifact changed, put `Next step` immediately after Study Note. Ordinary advisory work may use a short concrete action or state that none remains. For risky changes, explicit strict reports and structured receipts, build it from a quick whole-process scan with current situation, forward outlook, a concrete critical opinion, improvement recommendations, and ordered fix-first then planned actions with evidence and ownership. Keep report validation separate from actual security verification.
25
25
  23. Work on the current branch by default. Create or switch branches, create worktrees, or create pull requests only when the user explicitly requests that action or an applicable project rule explicitly requires it. A commit/push request, release version bump, route selection, branch-name prefix, or branch protection alone is not authorization. If protection blocks the requested operation, report the blocker and ask for direction; do not silently choose a PR workflow, bypass checks, or change protection settings.
26
26
 
27
+ ## Test check completion gate
28
+
29
+ After implementation and before claiming changed work complete, follow `references/test-check.md`: identify the exact starting revision, map changed targets to required scenarios, run the project Test check adapter and require a current PASS plus its explained note. Missing real DB/runtime evidence, skipped required scenarios, stale source/environment or note-write failures block completion approval. Report `Test check: BLOCKED` for an incomplete handoff; it does not grant commit/push/deployment approval. Recheck source identity before those operations. Do not treat report formatting, mock results, or operator-declared receipts as actual runtime verification. Projects need their own policy and adapters; an unavailable configuration is not a security or completion approval.
30
+
27
31
  ## Security check gate
28
32
 
29
33
  Before commit, push, CI approval, or deployment, run the configured `yam security check` gate for the exact stage and record its Security check block. Use `references/security-check.md`; preserve every SEC explanation, evidence reference, and unresolved status. Missing required evidence or a note-write failure blocks the stage. Local hooks are bypassable; enforce protected merge/deployment with a trusted CI policy and required checks. Never present a validated supplied report as an independent vulnerability scan.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,23 @@
2
2
 
3
3
  All notable changes to yam-flow are documented here.
4
4
 
5
+ ## v2.8.5 - 2026-10-28
6
+
7
+ ### Added
8
+
9
+ - Add the Test check completion gate with project-owned scenario policies, measured subprocess observations, source/environment bindings and private append-only notes.
10
+ - Require current Test check evidence before configured commit, push, CI and deployment security approval; CI verifies an administrator-reviewed test policy pin before executing project code.
11
+ - Add an optional isolated PostgreSQL simulation that reproduces SQLSTATE 42702 and checks normal writes, authorization, rollback, migration freshness and function drift using synthetic data.
12
+
13
+ ### Fixed
14
+
15
+ - Keep private `.yam` research receipts and temporary tools outside the public-product terminology scan while retaining checks for public source and documentation.
16
+
17
+ ### Verification
18
+
19
+ - 49 gate regression cases and full self-verification passed. Real PostgreSQL 17.10 simulations cover ten workflow scenarios and a 16-case role/brand/quantity matrix.
20
+ - Exact-target security integration and package checks passed locally. Production application behavior and hosted rollout require their own project evidence.
21
+
5
22
  ## v2.8.4 - 2026-10-26
6
23
 
7
24
  ### Improved
package/COMMANDS.md CHANGED
@@ -353,3 +353,7 @@ yam hook disable study-note --global
353
353
  - `yam template security` / `yam template security-ci`: explained note and CI setup templates.
354
354
 
355
355
  See `references/security-check.md` for stage arguments, trusted policy requirements and limitations. This validates supplied evidence; it does not automatically scan application vulnerabilities.
356
+
357
+ ## Test check
358
+
359
+ Before changed-work completion: `yam test-check plan --base <exact starting commit> --json`, then `yam test-check run --base <exact starting commit> --json` and `yam test-check check --json`. Configure `testing/policy.json` or `.yam/testing/policy.json` and a reviewed JSON-report adapter first. `yam template test-check` prints a blocked-until-configured starter. See `references/test-check.md` for scenario floors, real DB manifests, Stop integration and stage-specific identity checks. `check --stage commit` verifies index equality; push/CI/deploy require `--source <exact commit>` and CI/deploy also require a trusted `--policy-digest`. No global hook, branch protection or deployment is silently installed.
package/dist/bin/yam.js CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import { runTestCheckCommand, testCheckStop, testCheckConfigured } from '../lib/test-check.js';
2
3
  import { spawnSync } from 'node:child_process';
3
4
  import { studyNoteTurnScope } from '../lib/study-note-scope.js';
4
5
  import { selectHookNode, probeHookDiscovery, inspectHookObservation, recordHookObservation } from '../lib/hook-readiness.js';
@@ -149,6 +150,7 @@ Usage:
149
150
  yam proof [dir|--from file] [--route route] [--truth status] [--command text] [--evidence text]
150
151
  yam proof write [dir] [--format json|md] [--out file] [--route route] [--truth status] [--command text]
151
152
  yam study-note check [dir] [--report file|--text text] [--json]
153
+ yam test-check <plan|run|check|status> [--base commit] [--json]
152
154
  yam security <init|check|hooks|status> [--stage commit|push|ci|deploy] [--json]
153
155
  yam next-step <report|verify> [--spec file|--receipt file] [--json]
154
156
  yam loop report [--route route] [--intent text] [--stage id:status:note] [--evidence text] [--json]
@@ -175,7 +177,7 @@ Usage:
175
177
  yam safety [text...]
176
178
  yam memory <init|add|list|summary|resolve> [dir] [options]
177
179
  yam hook <status|enable|disable|run> [lite|study-note] [--global|--project dir]
178
- yam template <project|ueye|mission|proof|tuning|security|security-ci>
180
+ yam template <project|ueye|mission|proof|tuning|security|security-ci|test-check>
179
181
  yam tune-log [dir]
180
182
  yam install [--dry-run] [--json] [--replace-user-skill name]
181
183
  yam uninstall
@@ -495,6 +497,7 @@ async function verify({ quiet = false } = {}) {
495
497
  'markdown-management.md',
496
498
  'study-note.md',
497
499
  'security-check.md',
500
+ 'test-check.md',
498
501
  'final-report.md',
499
502
  'token-budget-reporter.md',
500
503
  'memory.md'
@@ -513,7 +516,7 @@ async function verify({ quiet = false } = {}) {
513
516
  issues.push(`project template missing section: ${section}`);
514
517
  }
515
518
  }
516
- for (const template of ['ueye-review.md', 'ueye-comparison.md', 'mission-plan.md', 'runtime-proof.md', 'tuning-log.md', 'security-check-note.md', 'security-gate.yml']) {
519
+ for (const template of ['ueye-review.md', 'ueye-comparison.md', 'mission-plan.md', 'runtime-proof.md', 'tuning-log.md', 'security-check-note.md', 'security-gate.yml', 'test-check-policy.json']) {
517
520
  if (!await exists(path.join(ROOT, 'templates', template)))
518
521
  issues.push(`missing template: ${template}`);
519
522
  }
@@ -2477,6 +2480,9 @@ async function buildYamLiteContext({ cwd, prompt }) {
2477
2480
  async function buildStudyNoteStopOutput(input = {}, cwd = process.cwd()) {
2478
2481
  const lastAssistantMessage = String(input?.last_assistant_message || input?.lastAssistantMessage || '');
2479
2482
  const scope = studyNoteTurnScope(input, cwd);
2483
+ const testGate = testCheckStop(cwd, !scope.available || scope.files.length > 0, lastAssistantMessage);
2484
+ if (testGate)
2485
+ return testGate;
2480
2486
  const result = await buildStudyNoteGuardResult(path.resolve(cwd), lastAssistantMessage, 'stop_hook_last_assistant_message', scope);
2481
2487
  if (result.truth_status === 'partial' && !result.changed_file_detection.available) {
2482
2488
  return {
@@ -2507,9 +2513,10 @@ async function buildStudyNoteStopOutput(input = {}, cwd = process.cwd()) {
2507
2513
  }
2508
2514
  async function buildStudyNoteTurnContext(input, cwd) {
2509
2515
  const scope = studyNoteTurnScope(input, cwd, true);
2516
+ const testReminder = testCheckConfigured(cwd) ? ' Test check active: before completion run yam test-check run --base <exact starting commit> --json and yam test-check check --json. Missing required tests/evidence block completion; report Test check: BLOCKED if unable to verify. This is separate from report formatting and Security check.' : '';
2510
2517
  if (!scope.available)
2511
2518
  return 'yam Study Note: turn scope is unavailable; do not treat this as a clean project. Inspect changed artifacts manually; no strict block will be applied without a baseline.';
2512
- return `yam Study Note turn baseline saved (hash-only). Mode: ${scope.mode}. Only changes since this prompt count; known generated logs, mission/security receipts and screenshots are excluded unless explicitly included. Include a concise Study Note followed by Next step when product artifacts change. ${scope.mode === 'strict' ? 'Risk work uses the detailed report contract and one correction pass.' : 'Ordinary work is advisory: a short explanation, verification/limits and remaining work (or none) suffice.'}`;
2519
+ return `yam Study Note turn baseline saved (hash-only). Mode: ${scope.mode}. Only changes since this prompt count; known generated logs, mission/security receipts and screenshots are excluded unless explicitly included. Include a concise Study Note followed by Next step when product artifacts change. ${scope.mode === 'strict' ? 'Risk work uses the detailed report contract and one correction pass.' : 'Ordinary work is advisory: a short explanation, verification/limits and remaining work (or none) suffice.'}${testReminder}`;
2513
2520
  }
2514
2521
  function yamLiteRouteHint(prompt = '') {
2515
2522
  const text = String(prompt || '').toLowerCase();
@@ -6132,11 +6139,12 @@ async function printTemplate(name = '') {
6132
6139
  runtime: 'runtime-proof.md',
6133
6140
  tuning: 'tuning-log.md',
6134
6141
  security: 'security-check-note.md',
6135
- 'security-ci': 'security-gate.yml'
6142
+ 'security-ci': 'security-gate.yml',
6143
+ 'test-check': 'test-check-policy.json'
6136
6144
  };
6137
6145
  const file = map[key];
6138
6146
  if (!file) {
6139
- console.error('usage: yam template <project|ueye|ueye-comparison|mission|proof|tuning|security|security-ci>');
6147
+ console.error('usage: yam template <project|ueye|ueye-comparison|mission|proof|tuning|security|security-ci|test-check>');
6140
6148
  process.exitCode = 1;
6141
6149
  return;
6142
6150
  }
@@ -6433,6 +6441,17 @@ function showRequestedHelp(args) {
6433
6441
  async function main() {
6434
6442
  const args = process.argv.slice(2);
6435
6443
  const command = args[0] || 'help';
6444
+ if (command === 'test-check') {
6445
+ if (!args[1] || ['help', '--help', '-h'].includes(args[1])) {
6446
+ console.log('yam test-check run --base <exact commit> [--policy file --policy-digest sha256] [--json]\nyam test-check check [--stage complete|commit|push|ci|deploy --source <exact commit> --policy-digest sha256 --receipt file] [--json]\nyam test-check status\nyam test-check plan --base <exact commit> --json\nSee references/test-check.md for required scenarios, adapters and activation.');
6447
+ return;
6448
+ }
6449
+ const result = runTestCheckCommand(args.slice(1));
6450
+ console.log(JSON.stringify(result, null, 2));
6451
+ if (!result.ok)
6452
+ process.exitCode = 1;
6453
+ return;
6454
+ }
6436
6455
  if (showRequestedHelp(args))
6437
6456
  return;
6438
6457
  if (command === 'help')
@@ -1,3 +1,4 @@
1
+ import { checkTestCheck, testCheckConfigured } from './test-check.js';
1
2
  import { createHash, randomUUID } from 'node:crypto';
2
3
  import { execFileSync } from 'node:child_process';
3
4
  import fs from 'node:fs/promises';
@@ -30,7 +31,7 @@ function parseArgs(args) {
30
31
  for (let i = 0; i < args.length; i++) {
31
32
  if (args[i] === '--json')
32
33
  continue;
33
- if (!['--policy', '--policy-digest', '--evidence', '--note', '--stage', '--remote', '--destination', '--source', '--base', '--artifact', '--artifact-digest', '--configuration', '--configuration-digest', '--environment', '--cli'].includes(args[i]) || !args[i + 1] || args[i + 1].startsWith('--') || out.has(args[i]))
34
+ if (!['--policy', '--policy-digest', '--evidence', '--note', '--stage', '--remote', '--destination', '--source', '--base', '--artifact', '--artifact-digest', '--configuration', '--configuration-digest', '--environment', '--cli', '--test-policy-digest'].includes(args[i]) || !args[i + 1] || args[i + 1].startsWith('--') || out.has(args[i]))
34
35
  throw new Error('invalid or duplicate security option');
35
36
  out.set(args[i], args[++i]);
36
37
  }
@@ -381,6 +382,14 @@ async function check(cwd, options, settings) {
381
382
  result = validateSecurityCheck(input, binding);
382
383
  gateErrors.push('security_input_or_binding_unavailable');
383
384
  }
385
+ if (testCheckConfigured(cwd) || config.test_policy_digest) {
386
+ const pin = options.get('--test-policy-digest') || config.test_policy_digest;
387
+ const sources = stage === 'push' ? (binding.push_refs || []).map(ref => ref.local_oid)
388
+ : [binding.source_revision];
389
+ const targets = sources.length ? sources : [undefined];
390
+ if (targets.some(source => !checkTestCheck(cwd, { stage, source, pin, base: stage === 'ci' ? binding.base_revision : undefined, artifact: options.get('--artifact'), configuration: options.get('--configuration') }).ok))
391
+ gateErrors.push('test_check_blocked');
392
+ }
384
393
  if (gateErrors.length) {
385
394
  result.result = 'BLOCKED';
386
395
  result.errors = [...new Set([...result.errors, ...gateErrors])];
@@ -0,0 +1,156 @@
1
+ export declare const TEST_REQUIREMENTS: {
2
+ readonly docs: readonly ["inspection"];
3
+ readonly logic: readonly ["success", "invalid", "boundary"];
4
+ readonly api: readonly ["success", "invalid", "contract"];
5
+ readonly db: readonly ["success", "denied", "invalid", "readback", "rollback", "migration_upgrade", "migration_fresh"];
6
+ readonly auth: readonly ["success", "denied", "cross_scope", "revoked"];
7
+ readonly ui: readonly ["success", "error"];
8
+ readonly release: readonly ["artifact", "integrity"];
9
+ };
10
+ type Risk = keyof typeof TEST_REQUIREMENTS;
11
+ type Check = {
12
+ id: string;
13
+ target: string;
14
+ scenario: string;
15
+ explanation: string;
16
+ evidence: 'static' | 'unit' | 'integration' | 'e2e';
17
+ command: string[];
18
+ environment?: string;
19
+ };
20
+ type Surface = {
21
+ id: string;
22
+ paths: string[];
23
+ risks: Risk[];
24
+ targets: string[];
25
+ checks: Check[];
26
+ };
27
+ type Policy = {
28
+ schema: 'yam.test-policy.v1';
29
+ surfaces: Surface[];
30
+ };
31
+ export declare function planTestCheck(root: string, policy: Policy, base: string): {
32
+ changed: string[];
33
+ source_digest: string;
34
+ checks: Check[];
35
+ errors: string[];
36
+ };
37
+ export declare function runTestCheck(cwd: string, options?: {
38
+ policy?: string;
39
+ pin?: string;
40
+ base?: string;
41
+ stage?: string;
42
+ source?: string;
43
+ artifact?: string;
44
+ configuration?: string;
45
+ }): {
46
+ ok: boolean;
47
+ result: string;
48
+ errors: any[];
49
+ run?: undefined;
50
+ note?: undefined;
51
+ receipt?: undefined;
52
+ changed_files?: undefined;
53
+ checks?: undefined;
54
+ } | {
55
+ ok: boolean;
56
+ result: any;
57
+ errors: any;
58
+ run: any;
59
+ note: string;
60
+ receipt: string;
61
+ changed_files: any;
62
+ checks: number;
63
+ };
64
+ export declare function checkTestCheck(cwd: string, options?: {
65
+ policy?: string;
66
+ pin?: string;
67
+ stage?: string;
68
+ source?: string;
69
+ receipt?: string;
70
+ artifact?: string;
71
+ configuration?: string;
72
+ base?: string;
73
+ }): {
74
+ ok: boolean;
75
+ result: string;
76
+ errors: string[];
77
+ run: any;
78
+ checks: number;
79
+ } | {
80
+ ok: boolean;
81
+ result: string;
82
+ errors: string[];
83
+ run?: undefined;
84
+ checks?: undefined;
85
+ };
86
+ export declare function testCheckConfigured(cwd: string): boolean;
87
+ export declare function testCheckStop(cwd: string, changed: boolean, message: string): {
88
+ continue: boolean;
89
+ systemMessage: string;
90
+ decision?: undefined;
91
+ reason?: undefined;
92
+ } | {
93
+ decision: string;
94
+ reason: string;
95
+ continue?: undefined;
96
+ systemMessage?: undefined;
97
+ };
98
+ export declare function runTestCheckCommand(args: string[], cwd?: string): {
99
+ ok: boolean;
100
+ result: string;
101
+ errors: any[];
102
+ run?: undefined;
103
+ note?: undefined;
104
+ receipt?: undefined;
105
+ changed_files?: undefined;
106
+ checks?: undefined;
107
+ } | {
108
+ ok: boolean;
109
+ result: any;
110
+ errors: any;
111
+ run: any;
112
+ note: string;
113
+ receipt: string;
114
+ changed_files: any;
115
+ checks: number;
116
+ } | {
117
+ ok: boolean;
118
+ result: string;
119
+ errors: string[];
120
+ run: any;
121
+ checks: number;
122
+ } | {
123
+ ok: boolean;
124
+ result: string;
125
+ errors: string[];
126
+ run?: undefined;
127
+ checks?: undefined;
128
+ } | {
129
+ checks: {
130
+ id: string;
131
+ target: string;
132
+ scenario: string;
133
+ explanation: string;
134
+ evidence: "static" | "unit" | "integration" | "e2e";
135
+ }[];
136
+ policy_digest: string;
137
+ changed: string[];
138
+ source_digest: string;
139
+ errors: string[];
140
+ ok: boolean;
141
+ } | {
142
+ ok: boolean;
143
+ result: string;
144
+ errors: string[];
145
+ run: any;
146
+ checks: number;
147
+ configured: boolean;
148
+ } | {
149
+ ok: boolean;
150
+ result: string;
151
+ errors: string[];
152
+ run?: undefined;
153
+ checks?: undefined;
154
+ configured: boolean;
155
+ };
156
+ export {};