yam-flow 0.1.9 → 1.9.1

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/CHANGELOG.md CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  All notable changes to yam-flow are documented here.
4
4
 
5
+ ## v1.9.1 - 2026-06-19
6
+
7
+ ### Added
8
+
9
+ - Extended `yam loop report --json` with guided stage conventions, `evidence_level`, `evidence_stamp`/`source_digest`, `blocked_kind`, `safe_retry`, `owner_scope`, `scope_owner`, and `side_effects` so the report can act as a clearer handoff artifact without adding a new command.
10
+ - Updated loop report smoke coverage to assert the new handoff fields.
11
+
12
+ ### Improved
13
+
14
+ - Updated README, COMMANDS, final-report guidance, implementation skill final-response rules, and manifest principles to prefer fix-first handoff with evidence level, blocker kind, safe retry, and owner/scope clarity.
15
+
5
16
  ## v0.1.9 - 2026-06-18
6
17
 
7
18
  ### Added
package/COMMANDS.md CHANGED
@@ -148,7 +148,7 @@ yam tools doctor /path/to/project
148
148
  yam tools doctor /path/to/project --json
149
149
  yam context pressure /path/to/project --json
150
150
  yam cleanup scan /path/to/project --json
151
- yam loop report --route quick --intent "fix release readiness" --stage "inspect:passed:read release report" --evidence "typecheck passed" --fix-first-item "npm auth must be verified before publish" --remaining-task "rerun release report after auth refresh" --recommended-direction "fix npm auth first, then publish manually" --implementation-note "keep loop report read-only" --why-this-next "auth blocks public release claims" --blocked-by "npm whoami E401" --owner-route deep --issue-code "src/bin/yam.ts release report" --issue-role "summarizes release readiness without publishing" --issue-symptom "npm auth failure needs clearer next action" --changed-code "yam loop report" --changed-role "records loop evidence and learning note" --change-summary "added a read-only loop artifact" --why-important "it helps users learn what changed without overclaiming verification" --learning-note "fix blockers before claiming done" --json
151
+ yam loop report --route quick --intent "fix release readiness" --stage "inspect:passed:read release report" --evidence "typecheck passed" --evidence-level local --evidence-stamp "sha256:release-report" --blocked-kind auth_blocked --safe-retry "retry after npm whoami succeeds" --fix-first-item "npm auth must be verified before publish" --remaining-task "rerun release report after auth refresh" --recommended-direction "fix npm auth first, then publish manually" --implementation-note "keep loop report read-only" --why-this-next "auth blocks public release claims" --blocked-by "npm whoami E401" --owner-route deep --owner-scope "release readiness only" --scope-owner deep --side-effect "no publish attempted" --issue-code "src/bin/yam.ts release report" --issue-role "summarizes release readiness without publishing" --issue-symptom "npm auth failure needs clearer next action" --changed-code "yam loop report" --changed-role "records loop evidence and learning note" --change-summary "added a read-only loop artifact" --why-important "it helps users learn what changed without overclaiming verification" --learning-note "fix blockers before claiming done" --json
152
152
  yam proof /path/to/project
153
153
  yam proof --route deep --truth verified --command "npm run build: pass"
154
154
  yam proof --route ueye --truth verified --visual "reference image only"
@@ -195,7 +195,7 @@ Supported report shapes:
195
195
 
196
196
  - Runtime evidence mini: command/process, observation, cleanup, truth status, next action.
197
197
  - Patch queue lite: mission lane status, changed files, verification hint, rollback hint, truth status.
198
- - Loop report: intent, stage outcomes, evidence, blockers, next action, fix-first items, remaining tasks, recommended direction, blocked-by notes, owner route, and study note.
198
+ - Loop report: intent, guided stage outcomes, evidence level/stamp, blockers, blocker kind, safe retry, next action, fix-first items, remaining tasks, recommended direction, blocked-by notes, owner route/scope, side effects, and study note.
199
199
  - Study note: short non-specialist explanation of the failing code, its role, observed symptom, changed code, and why it matters.
200
200
  - Benchmark optimization loop lite: baseline, one change, rerun, keep/revert decision.
201
201
  - Structured diagnostic next action: severity, evidence, owner route, next action, truth status.
package/README.md CHANGED
@@ -142,7 +142,7 @@ yam ueye report --reference ./reference.png --actual .yam/screens/home.png --pre
142
142
  yam proof --route ueye --truth verified --visual "implementation screenshot evidence recorded" --design-completion '{"completion_claim":"done","has_implementation_screenshot":true,"design_quality":"pass","states_checked":true,"mobile_checked":true,"contrast_checked":true,"cta_checked":true,"direction_locked":true,"truth_status":"verified"}'
143
143
  yam media proof --requested --attempted --output ./generated.png --wait-loop --json
144
144
  yam proof --route mission --mission-envelope '{"agent_id":"implementer","assigned_scope":"target component","changed_files":["src/file.ts"],"verification_hint":"npm run typecheck","truth_status":"partial"}'
145
- yam loop report --route quick --intent "fix release readiness" --stage "inspect:passed:read release report" --evidence "typecheck passed" --fix-first-item "npm auth must be verified before publish" --remaining-task "rerun release report after auth refresh" --recommended-direction "fix npm auth first, then publish manually" --implementation-note "keep loop report read-only" --why-this-next "auth blocks public release claims" --blocked-by "npm whoami E401" --owner-route deep --issue-code "src/bin/yam.ts release report" --issue-role "summarizes release readiness without publishing" --issue-symptom "npm auth failure needs clearer next action" --changed-code "yam loop report" --changed-role "records loop evidence and learning note" --change-summary "added a read-only loop artifact" --why-important "it helps users learn what changed without overclaiming verification" --learning-note "fix blockers before claiming done" --json
145
+ yam loop report --route quick --intent "fix release readiness" --stage "inspect:passed:read release report" --evidence "typecheck passed" --evidence-level local --evidence-stamp "sha256:release-report" --blocked-kind auth_blocked --safe-retry "retry after npm whoami succeeds" --fix-first-item "npm auth must be verified before publish" --remaining-task "rerun release report after auth refresh" --recommended-direction "fix npm auth first, then publish manually" --implementation-note "keep loop report read-only" --why-this-next "auth blocks public release claims" --blocked-by "npm whoami E401" --owner-route deep --owner-scope "release readiness only" --scope-owner deep --side-effect "no publish attempted" --issue-code "src/bin/yam.ts release report" --issue-role "summarizes release readiness without publishing" --issue-symptom "npm auth failure needs clearer next action" --changed-code "yam loop report" --changed-role "records loop evidence and learning note" --change-summary "added a read-only loop artifact" --why-important "it helps users learn what changed without overclaiming verification" --learning-note "fix blockers before claiming done" --json
146
146
  yam release report --json
147
147
  yam safety "supabase db reset"
148
148
  yam detect /path/to/project
@@ -216,7 +216,7 @@ These shapes keep reports machine-readable without turning normal work into a he
216
216
  - Runtime evidence mini: compact route, command/process, observation, cleanup, truth status, and next action fields for `$deep` or `$mission` runtime claims.
217
217
  - Patch queue lite: `$mission` lane records with pending/applied/verified/reverted/blocked state, changed files, verification hint, rollback hint, truth status, and next action.
218
218
  - Release report JSON: `yam release report --json` summarizes typecheck, forbidden-name scan, package boundary, registry status, CLI smoke, dist freshness, diagnostics, and final truth status.
219
- - Loop report JSON: `yam loop report --json` records a read-only handoff artifact: intent, stages, evidence, blockers, next action, fix-first items, remaining tasks, recommended direction, implementation notes, blocked-by notes, owner route, tool intent, truth status, and study note.
219
+ - Loop report JSON: `yam loop report --json` records a read-only handoff artifact: guided stages, evidence level/stamp, blocker kind, safe retry, owner route/scope, side effects, next action, fix-first items, remaining tasks, recommended direction, implementation notes, blocked-by notes, tool intent, truth status, and study note.
220
220
  - Study note: `yam.study-note.v1` keeps short non-specialist explanations for what code was failing, what role it plays, how the symptom showed up, what changed, and why it matters. Missing details stay in `limits` instead of being guessed.
221
221
  - Publish blocker evidence: release reports classify common npm auth, permission, OTP, immutable-version, cache, registry, and tarball failures into safe next actions.
222
222
  - Publish readiness evidence: release reports run read-only registry/auth/version probes, redact account/token details, and keep npm publish outside the report.
package/dist/bin/yam.js CHANGED
@@ -524,6 +524,12 @@ function normalizeToolIntent(value = '') {
524
524
  return text;
525
525
  return 'read_only';
526
526
  }
527
+ function normalizeLoopEvidenceLevelFlag(value = '') {
528
+ const text = String(value || '').toLowerCase().replace(/[-\s]/g, '_');
529
+ if (['fixture', 'smoke', 'local', 'real'].includes(text))
530
+ return text;
531
+ return 'none';
532
+ }
527
533
  function uniqueNextActionDetails(actions = []) {
528
534
  const seen = new Set();
529
535
  const unique = [];
@@ -1973,24 +1979,30 @@ function loopUsage() {
1973
1979
  Read-only loop artifact helpers. A loop report records intent, stages, evidence, blockers, next action, handoff fields, and a short study note. It does not run agents, start processes, publish packages, or write files.
1974
1980
 
1975
1981
  Usage:
1976
- yam loop report [--route route] [--intent text] [--loop-kind harness|release|ueye|scout|deep|mission] [--stage id:status:note] [--evidence text] [--blocked text] [--fix-first-item text] [--remaining-task text] [--recommended-direction text] [--implementation-note text] [--why-this-next text] [--blocked-by text] [--owner-route route] [--truth status] [--intent-label read_only|write|destructive|runtime|visual|publish] [--issue-code text] [--issue-role text] [--issue-symptom text] [--changed-code text] [--changed-role text] [--change-summary text] [--why-important text] [--learning-note text] [--json]
1982
+ yam loop report [--route route] [--intent text] [--loop-kind harness|release|ueye|scout|deep|mission] [--stage id:status:note] [--evidence text] [--evidence-level none|fixture|smoke|local|real] [--evidence-stamp text] [--source-digest text] [--blocked text] [--blocked-kind text] [--safe-retry text] [--fix-first-item text] [--remaining-task text] [--recommended-direction text] [--implementation-note text] [--why-this-next text] [--blocked-by text] [--owner-route route] [--owner-scope text] [--scope-owner text] [--side-effect text] [--truth status] [--intent-label read_only|write|destructive|runtime|visual|publish] [--issue-code text] [--issue-role text] [--issue-symptom text] [--changed-code text] [--changed-role text] [--change-summary text] [--why-important text] [--learning-note text] [--json]
1977
1983
  `);
1978
1984
  }
1979
1985
  async function loopReport(args = []) {
1980
1986
  if (args[0] === 'help' || args[0] === '--help' || args[0] === '-h')
1981
1987
  return loopUsage();
1982
- const flags = parseSimpleFlags(args, new Set(['route', 'intent', 'loop-kind', 'stage', 'evidence', 'blocked', 'blocker', 'next-action', 'fix-first-item', 'remaining-task', 'recommended-direction', 'direction', 'implementation-note', 'why-this-next', 'blocked-by', 'owner-route', 'truth', 'intent-label', 'tool-intent', 'issue-code', 'issue-role', 'issue-symptom', 'changed-code', 'changed-role', 'change-summary', 'why-important', 'learning-note', 'json']));
1988
+ const flags = parseSimpleFlags(args, new Set(['route', 'intent', 'loop-kind', 'stage', 'stage-convention', 'evidence', 'evidence-level', 'evidence-stamp', 'source-digest', 'blocked', 'blocker', 'blocked-kind', 'next-action', 'safe-retry', 'fix-first-item', 'remaining-task', 'recommended-direction', 'direction', 'implementation-note', 'why-this-next', 'blocked-by', 'owner-route', 'owner-scope', 'scope-owner', 'owner', 'scope', 'side-effect', 'truth', 'intent-label', 'tool-intent', 'issue-code', 'issue-role', 'issue-symptom', 'changed-code', 'changed-role', 'change-summary', 'why-important', 'learning-note', 'json']));
1983
1989
  const blockers = [...arrayFlag(flags.blocked), ...arrayFlag(flags.blocker)];
1984
1990
  const report = buildLoopReport({
1985
1991
  route: normalizeRoute(flags.route) || String(flags.route || ''),
1986
1992
  intent: String(flags.intent || ''),
1987
1993
  loop_kind: String(flags.loop_kind || flags.route || 'harness'),
1994
+ stage_conventions: arrayFlag(flags.stage_convention),
1988
1995
  stages: arrayFlag(flags.stage),
1989
1996
  evidence: arrayFlag(flags.evidence),
1997
+ evidence_level: normalizeLoopEvidenceLevelFlag(flags.evidence_level),
1998
+ evidence_stamp: String(flags.evidence_stamp || ''),
1999
+ source_digest: String(flags.source_digest || ''),
1990
2000
  blockers,
2001
+ blocked_kind: String(flags.blocked_kind || ''),
1991
2002
  truth_status: isTruthStatus(flags.truth) ? flags.truth : undefined,
1992
2003
  intent_label: normalizeToolIntent(flags.intent_label || flags.tool_intent || 'read_only'),
1993
2004
  next_action: String(flags.next_action || ''),
2005
+ safe_retry: String(flags.safe_retry || ''),
1994
2006
  fix_first_items: arrayFlag(flags.fix_first_item),
1995
2007
  remaining_tasks: arrayFlag(flags.remaining_task),
1996
2008
  recommended_direction: String(flags.recommended_direction || flags.direction || ''),
@@ -1998,6 +2010,9 @@ async function loopReport(args = []) {
1998
2010
  why_this_next: String(flags.why_this_next || ''),
1999
2011
  blocked_by: arrayFlag(flags.blocked_by),
2000
2012
  owner_route: String(flags.owner_route || flags.route || ''),
2013
+ owner_scope: [...arrayFlag(flags.owner_scope), ...arrayFlag(flags.scope)],
2014
+ scope_owner: String(flags.scope_owner || flags.owner || ''),
2015
+ side_effects: arrayFlag(flags.side_effect),
2001
2016
  issue_code: String(flags.issue_code || ''),
2002
2017
  issue_role: String(flags.issue_role || ''),
2003
2018
  issue_symptom: String(flags.issue_symptom || ''),
@@ -6,6 +6,7 @@ export type RuntimeSubsystem = 'real_runtime' | 'tmux_physical' | 'process_clean
6
6
  export type RuntimeBackend = 'none' | 'in_app_browser' | 'playwright' | 'terminal' | 'tmux' | 'zellij' | 'unknown';
7
7
  export type RuntimeClaim = 'not_started' | 'started' | 'observed' | 'stopped' | 'cleanup_verified' | 'blocked' | 'unknown';
8
8
  export type LoopStageStatus = 'passed' | 'failed' | 'blocked' | 'skipped' | 'partial' | 'pending' | 'recorded';
9
+ export type LoopEvidenceLevel = 'none' | 'fixture' | 'smoke' | 'local' | 'real';
9
10
  export type ToolIntent = 'read_only' | 'write' | 'destructive' | 'runtime' | 'visual' | 'publish';
10
11
  export interface SafetyHit {
11
12
  level: SafetyLevel;
@@ -154,13 +155,19 @@ export interface LoopReport {
154
155
  route: string;
155
156
  intent: string;
156
157
  loop_kind: string;
158
+ stage_conventions: string[];
157
159
  stages: LoopStage[];
158
160
  evidence: string[];
161
+ evidence_level: LoopEvidenceLevel;
162
+ evidence_stamp: string;
163
+ source_digest: string;
159
164
  blockers: string[];
165
+ blocked_kind: string;
160
166
  truth_status: TruthStatus;
161
167
  intent_label: ToolIntent;
162
168
  tool_intent: ToolIntent;
163
169
  next_action: string;
170
+ safe_retry: string;
164
171
  fix_first_items: string[];
165
172
  remaining_tasks: string[];
166
173
  recommended_direction: string;
@@ -168,6 +175,9 @@ export interface LoopReport {
168
175
  why_this_next: string;
169
176
  blocked_by: string[];
170
177
  owner_route: string;
178
+ owner_scope: string[];
179
+ scope_owner: string;
180
+ side_effects: string[];
171
181
  study_note: StudyNote;
172
182
  }
173
183
  export interface UeyeRunReport {
@@ -237,9 +237,14 @@ export function buildLoopReport(input = {}) {
237
237
  : [];
238
238
  const evidence = asList(input.evidence);
239
239
  const blockers = asList(input.blockers || input.blocked);
240
+ const blockedBy = asList(input.blocked_by);
241
+ const blockedKind = shortText(input.blocked_kind || (blockers.length || blockedBy.length ? 'evidence_missing' : ''));
242
+ const evidenceStamp = shortText(input.evidence_stamp || input.source_digest, 320);
243
+ const sourceDigest = shortText(input.source_digest || input.evidence_stamp, 320);
240
244
  const hasBlockedStage = stages.some((stage) => stage.status === 'blocked' || stage.status === 'failed');
245
+ const hasBlockerSignal = Boolean(blockers.length || blockedBy.length || blockedKind);
241
246
  const requestedTruth = isTruthStatus(input.truth_status) ? input.truth_status : '';
242
- const derivedTruth = blockers.length || hasBlockedStage ? 'blocked'
247
+ const derivedTruth = hasBlockerSignal || hasBlockedStage ? 'blocked'
243
248
  : evidence.length && stages.some((stage) => stage.status === 'passed') ? 'verified'
244
249
  : evidence.length || stages.length ? 'partial'
245
250
  : 'assumed';
@@ -252,20 +257,31 @@ export function buildLoopReport(input = {}) {
252
257
  route: String(input.route || ''),
253
258
  intent: shortText(input.intent),
254
259
  loop_kind: String(input.loop_kind || 'harness'),
260
+ stage_conventions: asList(input.stage_conventions || input.stage_convention).length
261
+ ? asList(input.stage_conventions || input.stage_convention)
262
+ : ['brief', 'plan', 'implement', 'critique', 'verify', 'handoff'],
255
263
  stages,
256
264
  evidence,
265
+ evidence_level: normalizeLoopEvidenceLevel(input.evidence_level),
266
+ evidence_stamp: evidenceStamp,
267
+ source_digest: sourceDigest,
257
268
  blockers,
269
+ blocked_kind: blockedKind,
258
270
  truth_status: truth,
259
271
  intent_label: intentLabel,
260
272
  tool_intent: intentLabel,
261
- next_action: shortText(input.next_action || (blockers[0] ? 'resolve the blocker before claiming this loop complete' : 'record the next smallest useful action')),
273
+ next_action: shortText(input.next_action || (hasBlockerSignal ? 'resolve the blocker before claiming this loop complete' : 'record the next smallest useful action')),
274
+ safe_retry: shortText(input.safe_retry || (hasBlockerSignal ? 'retry only after the blocker is resolved and new evidence is recorded' : 'not required')),
262
275
  fix_first_items: asList(input.fix_first_items || input.fix_first_item),
263
276
  remaining_tasks: asList(input.remaining_tasks || input.remaining_task),
264
277
  recommended_direction: shortText(input.recommended_direction || input.direction),
265
278
  implementation_notes: asList(input.implementation_notes || input.implementation_note),
266
279
  why_this_next: shortText(input.why_this_next),
267
- blocked_by: asList(input.blocked_by),
280
+ blocked_by: blockedBy,
268
281
  owner_route: normalizeOwnerRoute(input.owner_route || input.route),
282
+ owner_scope: asList(input.owner_scope || input.scope),
283
+ scope_owner: normalizeOwnerRoute(input.scope_owner || input.owner),
284
+ side_effects: asList(input.side_effects || input.side_effect),
269
285
  study_note: studyNote
270
286
  };
271
287
  }
@@ -400,6 +416,12 @@ function normalizeOwnerRoute(value = '') {
400
416
  return `$${text}`;
401
417
  return text ? shortText(text, 80) : '';
402
418
  }
419
+ function normalizeLoopEvidenceLevel(value = '') {
420
+ const text = String(value || '').toLowerCase().replace(/[-\s]/g, '_');
421
+ if (['fixture', 'smoke', 'local', 'real'].includes(text))
422
+ return text;
423
+ return 'none';
424
+ }
403
425
  function shortText(value = '', limit = 240) {
404
426
  const text = String(value || '').trim().replace(/\s+/g, ' ');
405
427
  if (text.length <= limit)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yam-flow",
3
- "version": "0.1.9",
3
+ "version": "1.9.1",
4
4
  "description": "Progressive proof-first agent flow: start fast, deepen deliberately, stay honest by design.",
5
5
  "type": "module",
6
6
  "author": "0kim0bos",
@@ -77,8 +77,11 @@ Use this when the next run needs a clear starting point:
77
77
  - Recommended direction.
78
78
  - Why this next step matters.
79
79
  - What is blocked, and by what.
80
+ - Evidence level or stamp when the handoff relies on a specific command, file, or report.
81
+ - Blocker kind and safe retry when the next run should not blindly repeat the same action.
82
+ - Owner route, scope owner, and side effects when more than one person or route may continue the work.
80
83
 
81
- `yam loop report` can record this as a handoff artifact with `fix_first_items`, `remaining_tasks`, `recommended_direction`, `implementation_notes`, `why_this_next`, `blocked_by`, and `owner_route`.
84
+ `yam loop report` can record this as a handoff artifact with `fix_first_items`, `remaining_tasks`, `recommended_direction`, `implementation_notes`, `why_this_next`, `blocked_by`, `blocked_kind`, `safe_retry`, `evidence_level`, `evidence_stamp`, `owner_route`, `owner_scope`, `scope_owner`, and `side_effects`.
82
85
 
83
86
  ## Route Weight
84
87
 
@@ -74,4 +74,4 @@ Include:
74
74
  - Evidence gathered.
75
75
  - Truth status.
76
76
  - Blockers or remaining risk.
77
- - Handoff: fix-first items, remaining tasks, recommended direction, and why that next step matters.
77
+ - Handoff: fix-first items, remaining tasks, recommended direction, why that next step matters, blocker kind, safe retry, evidence level/stamp, side effects, and owner scope when another route or person may continue.
@@ -106,4 +106,4 @@ Report:
106
106
  - Evidence gathered.
107
107
  - Truth status.
108
108
  - Cleanup status.
109
- - Handoff: fix-first items, remaining tasks, recommended direction, and why that next step matters.
109
+ - Handoff: fix-first items, remaining tasks, recommended direction, why that next step matters, blocker kind, safe retry, evidence level/stamp, side effects, and owner scope when another route or person may continue.
@@ -79,4 +79,4 @@ Keep it compact:
79
79
  - Study Note when useful: what code was wrong, what role it has, how the symptom showed up, what changed, and why it matters in short non-specialist language.
80
80
  - What was checked.
81
81
  - What was skipped, blocked, or still risky.
82
- - Handoff when useful: fix-first items, remaining tasks, recommended direction, and why that next step matters.
82
+ - Handoff when useful: fix-first items, remaining tasks, recommended direction, why that next step matters, blocker kind, safe retry, evidence level/stamp, and owner scope when another route or person may continue.
@@ -172,4 +172,4 @@ Report:
172
172
  - States/viewports checked.
173
173
  - Study Note when UI implementation changed code: what UI code changed, what role it has, how the visible symptom appeared, and why the change matters in short non-specialist language.
174
174
  - Truth status and visual verification cap.
175
- - Handoff when useful: fix-first visual issues, remaining tasks, recommended direction, and why that next step matters.
175
+ - Handoff when useful: fix-first visual issues, remaining tasks, recommended direction, why that next step matters, blocker kind, safe retry, evidence level/stamp, and owner scope when another route or person may continue.
package/src/bin/yam.ts CHANGED
@@ -24,7 +24,7 @@ import {
24
24
  detectDbSafetyText as detectTrustDbSafetyText,
25
25
  isTruthStatus
26
26
  } from '../lib/trust-kernel.js';
27
- import type { ToolIntent } from '../lib/trust-kernel.js';
27
+ import type { LoopEvidenceLevel, ToolIntent } from '../lib/trust-kernel.js';
28
28
 
29
29
  type AnyRecord = Record<string, any>;
30
30
 
@@ -525,6 +525,12 @@ function normalizeToolIntent(value = ''): ToolIntent {
525
525
  return 'read_only';
526
526
  }
527
527
 
528
+ function normalizeLoopEvidenceLevelFlag(value = ''): LoopEvidenceLevel {
529
+ const text = String(value || '').toLowerCase().replace(/[-\s]/g, '_');
530
+ if (['fixture', 'smoke', 'local', 'real'].includes(text)) return text as LoopEvidenceLevel;
531
+ return 'none';
532
+ }
533
+
528
534
  function uniqueNextActionDetails(actions = []) {
529
535
  const seen = new Set<string>();
530
536
  const unique = [];
@@ -1971,24 +1977,30 @@ function loopUsage() {
1971
1977
  Read-only loop artifact helpers. A loop report records intent, stages, evidence, blockers, next action, handoff fields, and a short study note. It does not run agents, start processes, publish packages, or write files.
1972
1978
 
1973
1979
  Usage:
1974
- yam loop report [--route route] [--intent text] [--loop-kind harness|release|ueye|scout|deep|mission] [--stage id:status:note] [--evidence text] [--blocked text] [--fix-first-item text] [--remaining-task text] [--recommended-direction text] [--implementation-note text] [--why-this-next text] [--blocked-by text] [--owner-route route] [--truth status] [--intent-label read_only|write|destructive|runtime|visual|publish] [--issue-code text] [--issue-role text] [--issue-symptom text] [--changed-code text] [--changed-role text] [--change-summary text] [--why-important text] [--learning-note text] [--json]
1980
+ yam loop report [--route route] [--intent text] [--loop-kind harness|release|ueye|scout|deep|mission] [--stage id:status:note] [--evidence text] [--evidence-level none|fixture|smoke|local|real] [--evidence-stamp text] [--source-digest text] [--blocked text] [--blocked-kind text] [--safe-retry text] [--fix-first-item text] [--remaining-task text] [--recommended-direction text] [--implementation-note text] [--why-this-next text] [--blocked-by text] [--owner-route route] [--owner-scope text] [--scope-owner text] [--side-effect text] [--truth status] [--intent-label read_only|write|destructive|runtime|visual|publish] [--issue-code text] [--issue-role text] [--issue-symptom text] [--changed-code text] [--changed-role text] [--change-summary text] [--why-important text] [--learning-note text] [--json]
1975
1981
  `);
1976
1982
  }
1977
1983
 
1978
1984
  async function loopReport(args = []) {
1979
1985
  if (args[0] === 'help' || args[0] === '--help' || args[0] === '-h') return loopUsage();
1980
- const flags = parseSimpleFlags(args, new Set(['route', 'intent', 'loop-kind', 'stage', 'evidence', 'blocked', 'blocker', 'next-action', 'fix-first-item', 'remaining-task', 'recommended-direction', 'direction', 'implementation-note', 'why-this-next', 'blocked-by', 'owner-route', 'truth', 'intent-label', 'tool-intent', 'issue-code', 'issue-role', 'issue-symptom', 'changed-code', 'changed-role', 'change-summary', 'why-important', 'learning-note', 'json']));
1986
+ const flags = parseSimpleFlags(args, new Set(['route', 'intent', 'loop-kind', 'stage', 'stage-convention', 'evidence', 'evidence-level', 'evidence-stamp', 'source-digest', 'blocked', 'blocker', 'blocked-kind', 'next-action', 'safe-retry', 'fix-first-item', 'remaining-task', 'recommended-direction', 'direction', 'implementation-note', 'why-this-next', 'blocked-by', 'owner-route', 'owner-scope', 'scope-owner', 'owner', 'scope', 'side-effect', 'truth', 'intent-label', 'tool-intent', 'issue-code', 'issue-role', 'issue-symptom', 'changed-code', 'changed-role', 'change-summary', 'why-important', 'learning-note', 'json']));
1981
1987
  const blockers = [...arrayFlag(flags.blocked), ...arrayFlag(flags.blocker)];
1982
1988
  const report = buildLoopReport({
1983
1989
  route: normalizeRoute(flags.route) || String(flags.route || ''),
1984
1990
  intent: String(flags.intent || ''),
1985
1991
  loop_kind: String(flags.loop_kind || flags.route || 'harness'),
1992
+ stage_conventions: arrayFlag(flags.stage_convention),
1986
1993
  stages: arrayFlag(flags.stage),
1987
1994
  evidence: arrayFlag(flags.evidence),
1995
+ evidence_level: normalizeLoopEvidenceLevelFlag(flags.evidence_level),
1996
+ evidence_stamp: String(flags.evidence_stamp || ''),
1997
+ source_digest: String(flags.source_digest || ''),
1988
1998
  blockers,
1999
+ blocked_kind: String(flags.blocked_kind || ''),
1989
2000
  truth_status: isTruthStatus(flags.truth) ? flags.truth : undefined,
1990
2001
  intent_label: normalizeToolIntent(flags.intent_label || flags.tool_intent || 'read_only'),
1991
2002
  next_action: String(flags.next_action || ''),
2003
+ safe_retry: String(flags.safe_retry || ''),
1992
2004
  fix_first_items: arrayFlag(flags.fix_first_item),
1993
2005
  remaining_tasks: arrayFlag(flags.remaining_task),
1994
2006
  recommended_direction: String(flags.recommended_direction || flags.direction || ''),
@@ -1996,6 +2008,9 @@ async function loopReport(args = []) {
1996
2008
  why_this_next: String(flags.why_this_next || ''),
1997
2009
  blocked_by: arrayFlag(flags.blocked_by),
1998
2010
  owner_route: String(flags.owner_route || flags.route || ''),
2011
+ owner_scope: [...arrayFlag(flags.owner_scope), ...arrayFlag(flags.scope)],
2012
+ scope_owner: String(flags.scope_owner || flags.owner || ''),
2013
+ side_effects: arrayFlag(flags.side_effect),
1999
2014
  issue_code: String(flags.issue_code || ''),
2000
2015
  issue_role: String(flags.issue_role || ''),
2001
2016
  issue_symptom: String(flags.issue_symptom || ''),
@@ -18,6 +18,7 @@ export type RuntimeSubsystem = 'real_runtime' | 'tmux_physical' | 'process_clean
18
18
  export type RuntimeBackend = 'none' | 'in_app_browser' | 'playwright' | 'terminal' | 'tmux' | 'zellij' | 'unknown';
19
19
  export type RuntimeClaim = 'not_started' | 'started' | 'observed' | 'stopped' | 'cleanup_verified' | 'blocked' | 'unknown';
20
20
  export type LoopStageStatus = 'passed' | 'failed' | 'blocked' | 'skipped' | 'partial' | 'pending' | 'recorded';
21
+ export type LoopEvidenceLevel = 'none' | 'fixture' | 'smoke' | 'local' | 'real';
21
22
  export type ToolIntent = 'read_only' | 'write' | 'destructive' | 'runtime' | 'visual' | 'publish';
22
23
 
23
24
  export interface SafetyHit {
@@ -182,13 +183,19 @@ export interface LoopReport {
182
183
  route: string;
183
184
  intent: string;
184
185
  loop_kind: string;
186
+ stage_conventions: string[];
185
187
  stages: LoopStage[];
186
188
  evidence: string[];
189
+ evidence_level: LoopEvidenceLevel;
190
+ evidence_stamp: string;
191
+ source_digest: string;
187
192
  blockers: string[];
193
+ blocked_kind: string;
188
194
  truth_status: TruthStatus;
189
195
  intent_label: ToolIntent;
190
196
  tool_intent: ToolIntent;
191
197
  next_action: string;
198
+ safe_retry: string;
192
199
  fix_first_items: string[];
193
200
  remaining_tasks: string[];
194
201
  recommended_direction: string;
@@ -196,6 +203,9 @@ export interface LoopReport {
196
203
  why_this_next: string;
197
204
  blocked_by: string[];
198
205
  owner_route: string;
206
+ owner_scope: string[];
207
+ scope_owner: string;
208
+ side_effects: string[];
199
209
  study_note: StudyNote;
200
210
  }
201
211
 
@@ -541,9 +551,14 @@ export function buildLoopReport(input: Partial<LoopReport> & Record<string, unkn
541
551
  : [];
542
552
  const evidence = asList(input.evidence);
543
553
  const blockers = asList(input.blockers || input.blocked);
554
+ const blockedBy = asList(input.blocked_by);
555
+ const blockedKind = shortText(input.blocked_kind || (blockers.length || blockedBy.length ? 'evidence_missing' : ''));
556
+ const evidenceStamp = shortText(input.evidence_stamp || input.source_digest, 320);
557
+ const sourceDigest = shortText(input.source_digest || input.evidence_stamp, 320);
544
558
  const hasBlockedStage = stages.some((stage) => stage.status === 'blocked' || stage.status === 'failed');
559
+ const hasBlockerSignal = Boolean(blockers.length || blockedBy.length || blockedKind);
545
560
  const requestedTruth = isTruthStatus(input.truth_status) ? input.truth_status : '';
546
- const derivedTruth: TruthStatus = blockers.length || hasBlockedStage ? 'blocked'
561
+ const derivedTruth: TruthStatus = hasBlockerSignal || hasBlockedStage ? 'blocked'
547
562
  : evidence.length && stages.some((stage) => stage.status === 'passed') ? 'verified'
548
563
  : evidence.length || stages.length ? 'partial'
549
564
  : 'assumed';
@@ -556,20 +571,31 @@ export function buildLoopReport(input: Partial<LoopReport> & Record<string, unkn
556
571
  route: String(input.route || ''),
557
572
  intent: shortText(input.intent),
558
573
  loop_kind: String(input.loop_kind || 'harness'),
574
+ stage_conventions: asList(input.stage_conventions || input.stage_convention).length
575
+ ? asList(input.stage_conventions || input.stage_convention)
576
+ : ['brief', 'plan', 'implement', 'critique', 'verify', 'handoff'],
559
577
  stages,
560
578
  evidence,
579
+ evidence_level: normalizeLoopEvidenceLevel(input.evidence_level),
580
+ evidence_stamp: evidenceStamp,
581
+ source_digest: sourceDigest,
561
582
  blockers,
583
+ blocked_kind: blockedKind,
562
584
  truth_status: truth,
563
585
  intent_label: intentLabel,
564
586
  tool_intent: intentLabel,
565
- next_action: shortText(input.next_action || (blockers[0] ? 'resolve the blocker before claiming this loop complete' : 'record the next smallest useful action')),
587
+ next_action: shortText(input.next_action || (hasBlockerSignal ? 'resolve the blocker before claiming this loop complete' : 'record the next smallest useful action')),
588
+ safe_retry: shortText(input.safe_retry || (hasBlockerSignal ? 'retry only after the blocker is resolved and new evidence is recorded' : 'not required')),
566
589
  fix_first_items: asList(input.fix_first_items || input.fix_first_item),
567
590
  remaining_tasks: asList(input.remaining_tasks || input.remaining_task),
568
591
  recommended_direction: shortText(input.recommended_direction || input.direction),
569
592
  implementation_notes: asList(input.implementation_notes || input.implementation_note),
570
593
  why_this_next: shortText(input.why_this_next),
571
- blocked_by: asList(input.blocked_by),
594
+ blocked_by: blockedBy,
572
595
  owner_route: normalizeOwnerRoute(input.owner_route || input.route),
596
+ owner_scope: asList(input.owner_scope || input.scope),
597
+ scope_owner: normalizeOwnerRoute(input.scope_owner || input.owner),
598
+ side_effects: asList(input.side_effects || input.side_effect),
573
599
  study_note: studyNote
574
600
  };
575
601
  }
@@ -694,6 +720,12 @@ function normalizeOwnerRoute(value: unknown = ''): string {
694
720
  return text ? shortText(text, 80) : '';
695
721
  }
696
722
 
723
+ function normalizeLoopEvidenceLevel(value: unknown = ''): LoopEvidenceLevel {
724
+ const text = String(value || '').toLowerCase().replace(/[-\s]/g, '_');
725
+ if (['fixture', 'smoke', 'local', 'real'].includes(text)) return text as LoopEvidenceLevel;
726
+ return 'none';
727
+ }
728
+
697
729
  function shortText(value: unknown = '', limit = 240): string {
698
730
  const text = String(value || '').trim().replace(/\s+/g, ' ');
699
731
  if (text.length <= limit) return text;
package/yam.manifest.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yam",
3
- "version": "0.1.9",
3
+ "version": "1.9.1",
4
4
  "principles": [
5
5
  "Direction before execution.",
6
6
  "Start fast.",
@@ -12,7 +12,7 @@
12
12
  "Context-reuse first.",
13
13
  "End with remaining tasks and fix-first items.",
14
14
  "Explain what failed, what changed, and why it matters in short non-specialist language when useful.",
15
- "Use handoff notes for fix-first items, remaining tasks, and recommended next direction when useful."
15
+ "Use handoff notes for fix-first items, remaining tasks, recommended next direction, blocker kind, safe retry, evidence level, and owner scope when useful."
16
16
  ],
17
17
  "defaultHooks": false,
18
18
  "routes": [