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 +4 -0
- package/CHANGELOG.md +17 -0
- package/COMMANDS.md +4 -0
- package/dist/bin/yam.js +24 -5
- package/dist/lib/security-integration.js +10 -1
- package/dist/lib/test-check.d.ts +156 -0
- package/dist/lib/test-check.js +558 -0
- package/package.json +5 -2
- package/references/final-report.md +4 -0
- package/references/test-check.md +192 -0
- package/skills/deep/SKILL.md +4 -0
- package/skills/mission/SKILL.md +4 -0
- package/skills/quick/SKILL.md +4 -0
- package/skills/ueye/SKILL.md +4 -0
- package/src/bin/yam.ts +19 -5
- package/src/lib/security-integration.ts +9 -1
- package/src/lib/test-check.ts +348 -0
- package/templates/test-check-policy.json +53 -0
- package/yam.manifest.json +1 -1
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 {};
|