gennady 0.8.4-next.3 → 0.8.4-next.8

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.
@@ -49,7 +49,7 @@
49
49
  Deterministic mechanical checks — file-header presence, Task-ID collision, tracker-sync — are NOT hand-coded here. They live in one tool, `sdd check`, which sdd-check (whole tree) and this directive (scoped) both consume. Single source of mechanical truth → no drift between the two SDD reviewers.
50
50
 
51
51
  - `sdd check --files <git-diff in-scope files>` → [HEADERS] (`@file`/`@consumers`/`@tasks` presence).
52
- - `sdd check --task <Task-ID>` → [TASKID] (collision for that id) + [TRACKER_SYNC] (Meta.Status vs tracker row).
52
+ - `sdd check --task <Task-ID>` → [TASKID] (collision for that id) + [TRACKER_SYNC] (Meta.Status vs tracker row) + [RULES] (four-section presence in the rules this ticket cites; `rule_findings` is counted apart from `findings` because a shared rule file is not this task's defect).
53
53
  - `sdd check <root>` (no flag) → tree-wide [TASKID] orphan refs (epic mode).
54
54
 
55
55
  Audit still owns everything the tool cannot decide mechanically: semantic `@consumers` resolvability, current-Task-ID identity, append-only `@tasks` regression vs git, section-anchor coverage, and all code-reading checks (closed-world, completeness, rules-compliance, runtime-backing, backflow).
@@ -67,7 +67,7 @@
67
67
  | `RULES_CASCADE_MISMATCH` | a phase's `Rules:` list ≠ what scaffolder would derive now from cascade × that phase's `Target Files` + kind |
68
68
  | `TASK_ID_DRIFT` | `@tasks` field references non-existent ticket ID (orphan); OR Task-ID duplicated across ticket files (collision); OR `@tasks` field lost prior IDs (regression of append-only) |
69
69
  | `BDD_COVERAGE_MISMATCH` | canonical case names in ticket Test Scenario Coverage ≠ real test cases |
70
- | `EXECUTION_LOG_INCOMPLETE` | mandatory closing lines missing (`ver` / `DONE` per phase, `DONE` at Round close); OR `intro` line absent for an entity that appears in code but not in module Inventory; OR a previous round was edited (round log is append-only); OR `[x]` line with unreplaced `<…>` placeholder (fabricated done) |
70
+ | `EXECUTION_LOG_INCOMPLETE` | mandatory closing lines missing (`ver` / `DONE` per phase, `DONE` at Round close); OR `intro` line absent for an entity that appears in code but not in module Inventory; OR a previous round was edited (round log is append-only); OR `[x]` line with unreplaced `<…>` placeholder (fabricated done). Token vocabulary and Round-close shape are decided by `sdd check` [LOG], not by eye: `unknown-token` / `unclosed-round` are findings, `retired-token` / `round-close-no-timestamp` are not (append-only history) |
71
71
  | `INSIGHT_BACKFLOW` | Phase agent discovered something that changes contract / requirement understanding; should flow back into spec |
72
72
  | `STALE_AFTER_PIVOT` | scope spec was reworked in `pivot` mode (Pivot Invalidation List exists) but downstream artifacts named in the list have not been refined / reopened / updated |
73
73
  | `RULE_FILE_INCOMPLETE` | activated rule file lacks one of the universal checkable sections (`<BeliefState>` / `<AntiPatterns>` / `<VerificationHooks>` / `<RewardCriteria>`) per `AX_RULES_COMPLIANCE_AGAINST_ACTIVATED_RULES`; audit can partially proceed but the gap blocks full coverage |
@@ -87,10 +87,11 @@
87
87
  </Axiom>
88
88
 
89
89
  <Axiom id="AX_FINDINGS_AS_PROPOSALS">
90
- Every finding contains a proposed remediation. Agent does not modify code. Three remediation types:
90
+ Every finding contains a proposed remediation. Agent does not modify code. Remediation types:
91
91
  - `code-fix` — concrete change in code (with location).
92
92
  - `spec-update` — concrete spec update (module or scope) with proposed diff.
93
93
  - `ticket-update` — ticket update (canonical case names, deferred scope, etc.).
94
+ - `rule-file-fix` — shared rule/directive/tooling artifact; outside any task, goes to its own ticket.
94
95
  </Axiom>
95
96
 
96
97
  <Axiom id="AX_FINDING_ROUTING">
@@ -108,6 +109,7 @@
108
109
  | `INSIGHT_BACKFLOW` | spec edit (scope or module spec) |
109
110
  | `STALE_AFTER_PIVOT` | reopen / refine the affected downstream artifact |
110
111
  | `TASK_ID_DRIFT` | code-fix via ticket reopen |
112
+ | `RULE_FILE_INCOMPLETE` | `rule-file-fix` — the rule file is shared project infrastructure, outside every phase's Target Files. Never `ticket-update` (the ticket cannot fix it, and the paper-fix loops forever), never `phases_to_fix`, never `FAIL` for this task |
111
113
  | operator-acknowledged risk | Decision Log entry in the scope spec |
112
114
 
113
115
  The audit agent proposes a concrete edit (diff or instruction). The operator decides and applies. The audit agent does not modify code autonomously (per `AX_NO_AUTO_FIX`).
@@ -170,7 +172,7 @@
170
172
  - `<VerificationHooks>` → executable check commands
171
173
  - `<RewardCriteria>` → ✅ invariants / ❌ forbidden states
172
174
 
173
- Required sections: `<BeliefState>` / `<AntiPatterns>` / `<VerificationHooks>` / `<RewardCriteria>`. Optional: `<DependsOn>` (only when the rule extends another). Required section missing → `RULE_FILE_INCOMPLETE` (`MAJOR`); audit proceeds with available sections.
175
+ Section presence is NOT judged by eye — `sdd check --task <Task-ID>` emits `[RULES]` for exactly this ticket's cited rules (per `AX_MECHANICAL_VIA_SDD_CHECK`). Each `INCOMPLETE` row → one `RULE_FILE_INCOMPLETE`, its `missing` column copied verbatim. Audit proceeds with the sections that exist. `<DependsOn>` is optional and unchecked.
174
176
 
175
177
  **Per (artifact ∈ Round diff, rule ∈ union of all phases' Rules):**
176
178
  1. Apply rule's `<Triggers>` / `<ActivationHint>` from `knowledge.xml` to decide if the rule governs this artifact. No match → skip pair.
@@ -458,7 +460,7 @@
458
460
  <Contract id="FINDING_FORMAT">
459
461
  ```markdown
460
462
  ### <🔴|🟠|🟡|🔵> F-NNN — <short title>
461
- - **Type:** CLOSED_WORLD_DRIFT | COMPLETENESS_GAP | RUNTIME_BACKING_VIOLATION | RULES_COMPLIANCE_VIOLATION | RULES_CASCADE_MISMATCH | TASK_ID_DRIFT | BDD_COVERAGE_MISMATCH | EXECUTION_LOG_INCOMPLETE | STALE_AFTER_PIVOT | INSIGHT_BACKFLOW
463
+ - **Type:** CLOSED_WORLD_DRIFT | COMPLETENESS_GAP | RUNTIME_BACKING_VIOLATION | RULES_COMPLIANCE_VIOLATION | RULES_CASCADE_MISMATCH | TASK_ID_DRIFT | BDD_COVERAGE_MISMATCH | EXECUTION_LOG_INCOMPLETE | STALE_AFTER_PIVOT | INSIGHT_BACKFLOW | RULE_FILE_INCOMPLETE
462
464
  - **Severity:** 🔴 BLOCKER | 🟠 MAJOR | 🟡 MINOR | 🔵 INFO
463
465
  - **Confidence:** HIGH | MEDIUM | LOW
464
466
  - **Status:** 📂 open | ⚠️ operator-acknowledged | ✅ resolved
@@ -482,7 +484,7 @@
482
484
 
483
485
  ```
484
486
  @audit task=<Task-ID> round=<N> mode=<per-task|epic> status=<PASS|PASS_RISK|FAIL> counts=B<n>·M<n>·m<n>·I<n> phases_to_fix=[<P<N>>,...]
485
- F-<NN> | sev=<B|M|m|I> | type=<TYPETOKEN> | conf=<H|M|L> | loc=<path:line|—> | phase=<P<N>|—> | src=<spec/ticket/rule anchor|—> | route=<spec-edit|ticket-reopen|ticket-update|decision-log|code-fix> | act=<one-line action, no newlines>
487
+ F-<NN> | sev=<B|M|m|I> | type=<TYPETOKEN> | conf=<H|M|L> | loc=<path:line|—> | phase=<P<N>|—> | src=<spec/ticket/rule anchor|—> | route=<spec-edit|ticket-reopen|ticket-update|decision-log|code-fix|rule-file-fix> | act=<one-line action, no newlines>
486
488
  F-<NN> | …
487
489
  ~applied | <target> | <one-line description of inline change> (optional, repeat per applied change)
488
490
  ```
@@ -490,8 +492,8 @@
490
492
  Token vocabulary:
491
493
  - `sev`: `B`=BLOCKER, `M`=MAJOR, `m`=MINOR, `I`=INFO.
492
494
  - `conf`: `H`=HIGH, `M`=MEDIUM, `L`=LOW.
493
- - `type`: one of `CLOSED_WORLD_DRIFT` | `COMPLETENESS_GAP` | `RUNTIME_BACKING_VIOLATION` | `RULES_COMPLIANCE_VIOLATION` | `RULES_CASCADE_MISMATCH` | `TASK_ID_DRIFT` | `BDD_COVERAGE_MISMATCH` | `EXECUTION_LOG_INCOMPLETE` | `STALE_AFTER_PIVOT` | `INSIGHT_BACKFLOW`.
494
- - `route`: one of `spec-edit` | `ticket-reopen` | `ticket-update` | `decision-log` | `code-fix` (per `AX_FINDING_ROUTING`).
495
+ - `type`: one of `CLOSED_WORLD_DRIFT` | `COMPLETENESS_GAP` | `RUNTIME_BACKING_VIOLATION` | `RULES_COMPLIANCE_VIOLATION` | `RULES_CASCADE_MISMATCH` | `TASK_ID_DRIFT` | `BDD_COVERAGE_MISMATCH` | `EXECUTION_LOG_INCOMPLETE` | `STALE_AFTER_PIVOT` | `INSIGHT_BACKFLOW` | `RULE_FILE_INCOMPLETE`.
496
+ - `route`: one of `spec-edit` | `ticket-reopen` | `ticket-update` | `decision-log` | `code-fix` | `rule-file-fix` (per `AX_FINDING_ROUTING`).
495
497
  - `act` text: Russian operator-facing per `AX_OPERATOR_LANGUAGE` (this is operator-facing artifact text), single line, no pipe character.
496
498
  - `status` `PASS_RISK` = PASS_WITH_ACKNOWLEDGED_RISKS.
497
499
 
@@ -28,6 +28,8 @@
28
28
  <BeliefState>
29
29
  <Axiom id="AX_PHASE_SCOPE_LOCK">
30
30
  Touch only this phase's `Target Files`. Reading other files (specs, sibling-phase code, rule files cited in this phase's Rules list) is allowed and expected. Writing anywhere outside this phase's Target Files → `H_OUT_OF_PHASE_WRITE`. Operator decides via re-planning or new task.
31
+
32
+ This bounds ownership of verification failures too. A repo-wide gate that fails inside ANOTHER phase's `Target Files` is that phase's work: do not write there, do not call it resolved, do not treat it as your blocker. Record it in Handoff `open:` and continue. Everywhere else the failure is yours — including a file you never opened whose build your diff broke. Nothing outside this ticket's phases is covered by either case: that is `AX_BLOCKER_ESCALATION`.
31
33
  </Axiom>
32
34
 
33
35
 
@@ -73,7 +75,7 @@
73
75
 
74
76
  <Axiom id="AX_PERMITTED_BASH_COMMANDS">
75
77
  Phase agent may ONLY run these bash commands:
76
- - **Must run:** `<sdd-path> verify <target-files>` — MANDATORY. Auto-discovers and runs typecheck, gennady DBC lint, linter, tests, and format check for the project. Runs before §5 commands. **RUN-ALL**: every gate executes regardless of previous failures; failures accumulate. **SUPPRESS-ON-SUCCESS**: passing gates produce zero output; only failed gates dump their command, exit code, and captured output. On all-pass: single summary line. Failing gate → fix and re-run before EMIT_HANDOFF. Log each passing gate on a separate `ver` line.
78
+ - **Must run:** `<sdd-path> verify <target-files>` — MANDATORY. Auto-discovers and runs typecheck, gennady DBC lint, linter, tests, and format check for the project. Runs before §5 commands. **RUN-ALL**: every gate executes regardless of previous failures; failures accumulate. **SUPPRESS-ON-SUCCESS**: passing gates produce zero output; only failed gates dump their command, exit code, and captured output. On all-pass: single summary line. Failing gate → fix and re-run before EMIT_HANDOFF. ONE `ver` line per invocation (see `STEP_5_VERIFY`).
77
79
  - **Must run:** verification commands from ticket §5 that match this phase's Rules (per `AX_VERIFICATION_BEFORE_HANDOFF`).
78
80
  - **May run:** `ls <dir>` for targeted recon (NOT `-la` or recursive); `tsc --noEmit` after code changes; `node --test <specific test file>` for test-kind phases; `date -u +%Y-%m-%dT%H:%M:%SZ` for timestamps.
79
81
  - **May run:** `<sdd-path> extract <file> <NAME>` — extract anchored section from ticket/spec.
@@ -280,11 +282,9 @@
280
282
  <Step id="STEP_5_VERIFY">
281
283
  <Goal>Run MANDATORY sdd verify on target files, then ticket §5 commands. Log only final results.</Goal>
282
284
  <Action>
283
- 1. **MANDATORY — sdd verify gate:** Run `<sdd-path> verify <target-files>`. This auto-discovers and executes typecheck, gennady DBC lint, linter, tests, and format check from package.json scripts. **RUN-ALL**: every gate executes regardless of previous failures; failures accumulate. **SUPPRESS-ON-SUCCESS**: passing gates produce zero output; only failed gates dump their command, exit code, and captured output. On all-pass: single summary line. If any gate fails → fix ALL failures and re-run the full `sdd verify` again. Do NOT proceed to EMIT_HANDOFF until all gates pass.
284
-
285
- **⚠️ ERROR OWNERSHIP (MANDATORY):** Every error surfaced by `<sdd-path> verify` is an error of the current session. The agent is the sole actor in this repository during the session. No error may be dismissed as «not my file», «pre-existing», «someone else's problem», or «out of scope». If `<sdd-path> verify` reports a failure in ANY file — edited, created, never touched, outside Target Files, config, spec, task, generated — the agent owns it and MUST fix it. There is no «their error». There are only errors the agent has not yet fixed. Verification is holistic; so is ownership. Fix everything.
285
+ 1. **MANDATORY — sdd verify gate:** Run `<sdd-path> verify <target-files>`. This auto-discovers and executes typecheck, gennady DBC lint, linter, tests, and format check from package.json scripts. **RUN-ALL**: every gate executes regardless of previous failures; failures accumulate. **SUPPRESS-ON-SUCCESS**: passing gates produce zero output; only failed gates dump their command, exit code, and captured output. On all-pass: single summary line. Any failure is yours to fix unless `AX_PHASE_SCOPE_LOCK` puts the file in another phase's hands — «pre-existing» and «not my file» are not available for the rest. Fix, re-run the full `sdd verify`, and do not proceed to EMIT_HANDOFF while a gate you own is red.
286
286
 
287
- Each passing gate gets its own `ver` line.
287
+ Log ONE `ver` line for this invocation, quoting the tool's own verdict — `sdd verify` names only the gates that FAILED, so a per-gate «pass» line reports a result it never printed. That is `fabricated-verification`, same BLOCKER class as logging a §5 command you did not run.
288
288
  2. Then run ticket §5 commands per existing logic below.
289
289
  2. **MANDATORY (PROTOCOL):** Execute EACH such §5 command **verbatim — the exact string from the ticket**. No substitutions, no "equivalents", no narrower variants. If §5 says `npm run check`, run `npm run check` (not `npx vitest`, not `tsc --noEmit`, not `<sdd-path> verify`). This is the Canonical Gate (per `AX_VERIFICATION_BEFORE_HANDOFF`); fabrication is a BLOCKER-class finding at audit.
290
290
  3. **Supplemental (OPTIONAL):** You MAY additionally run `<sdd-path> verify <Target Files>` or `tsc --noEmit` for narrower diagnostics — useful for fast feedback or DBC lint. These do NOT replace §5; they add to it. Each supplemental run is logged on its own `ver` line with its real command.
@@ -229,7 +229,9 @@
229
229
  </Axiom>
230
230
 
231
231
  <Axiom id="AX_AUDIT_HOOK">
232
- Round is complete only after `audit` returns PASS. `sdd-execute` orchestrator dispatches audit-subagent automatically after the last phase of a Round closes — operator does not invoke audit manually. `[x] DONE` on the ticket is mechanical close, not verification. Until audit PASS, dependents are blocked.
232
+ Round is complete only after `audit` returns PASS. `sdd-execute` orchestrator dispatches audit-subagent automatically after the last phase of a Round closes — operator does not invoke audit manually.
233
+
234
+ Closing the Round does NOT set `[x] DONE`. The ticket stays `[~] IN_PROGRESS` from Round close until audit PASS, and only then becomes `[x] DONE`. So `[x] DONE` means verified — one meaning, and the one dependents rely on: pickability reads that status, and a task whose audit has not run (or has failed) must not read as pickable.
233
235
  </Axiom>
234
236
 
235
237
  <Axiom id="AX_PHASES_DECLARED_IN_HEADER">
@@ -714,7 +716,7 @@
714
716
  ⛔ `[x]` line with any unreplaced `<…>` literal = fabricated done → `EXECUTION_LOG_INCOMPLETE` (BLOCKER).
715
717
 
716
718
  ### Post-task Hook
717
- Per `AX_AUDIT_HOOK`. After last phase of a Round closes, the orchestrator dispatches `sdd-audit`. Until audit returns PASS, Round is closed-but-unverified and dependents are blocked.
719
+ Per `AX_AUDIT_HOOK`. After last phase of a Round closes, the orchestrator dispatches `sdd-audit`. Round close leaves the ticket `[~] IN_PROGRESS`; audit PASS is what sets `[x] DONE`. Until then dependents are blocked, and the status says so.
718
720
 
719
721
  ## High-Level DAG
720
722
  Cross-scope edges + integration tickets only. Intra-scope DAGs live in per-scope READMEs.
@@ -81,10 +81,16 @@ Parse `Dependencies:` from each task ticket planning surface. Topological sort.
81
81
 
82
82
  From scan [TASKS] output: check `placeholders` column for any task with >0. Flag tasks where placeholders > 0 even if status DONE. Also inspect `warnings` column for `no-execlog-section` or `anchors-mismatch`.
83
83
 
84
+ Token vocabulary and Round-close shape come from `sdd check` [LOG] — do not re-read logs by hand. `unknown-token` (outside the scaffold table) and `unclosed-round` (a Round close with no ticked DONE) → FAIL. `retired-token` (valid before the vocabulary was consolidated) and `round-close-no-timestamp` → INFO: rounds are append-only, so history cannot be rewritten to satisfy a later rule.
85
+
84
86
  ### Check 5b — Task-ID Integrity (from `sdd check` [TASKID])
85
87
 
86
88
  Read the [TASKID] section of `sdd check`. `collision` (one Task-ID on ≥2 ticket files) → FAIL (BLOCKER). `orphan` (a code `@tasks: TSK-NN` with no ticket file) → FAIL. Empty section → PASS. Same tool sdd-audit STEP_2_5 uses.
87
89
 
90
+ ### Check 5c — Rule File Schema (from `sdd check` [RULES])
91
+
92
+ Read the [RULES] section. Each `verdict=INCOMPLETE` row is a rule file missing one of the four checkable sections; the `missing` column names them. Report as INFO with the file list, **not** as a tree FAIL: these are shared project artifacts, tracked by `rule_findings` separately from `findings` for exactly that reason. Fixing them is its own ticket, never a task reopen.
93
+
88
94
  ### Check 6 — File Headers (from `sdd check --files`)
89
95
 
90
96
  Pass recently modified source files to the shared checker instead of sampling by hand:
@@ -134,8 +140,9 @@ First: Self-Reflection. Then: Mechanical Checks. Use compact single-line-per-che
134
140
 
135
141
  Rules for VERDICT line:
136
142
 
137
- - All 8 checks PASS AND 0 protocol violations → `✅ CLEAN — artifact tree is consistent, next pickable: TSK-NN`
138
- - All 8 checks PASS AND 0 violations but some tasks deferred/TODO → `✅ CLEAN — <N> tasks remaining in queue`
143
+ - All checks PASS AND 0 protocol violations → `✅ CLEAN — artifact tree is consistent, next pickable: TSK-NN`
144
+ - All checks PASS AND 0 violations but some tasks deferred/TODO → `✅ CLEAN — <N> tasks remaining in queue`
145
+ - Check 5c findings are INFO — they never move the verdict off `✅ CLEAN`; append `· <N> rule file(s) incomplete` to the line instead
139
146
  - Any FAIL check OR protocol violations → `❌ NOT READY — <N> issue(s) require attention`
140
147
  - Checks skipped (marked `—`) → treat as PASS for verdict unless evidence of gap exists
141
148
  </Output>
@@ -95,16 +95,16 @@ Pause path (distinguish from failure — skill is awaiting operator, not broken)
95
95
  4. **Close Round** — append to ticket section 7:
96
96
  ```
97
97
  #### Round close
98
- - [x] `<ts>` sync <scope>+root
99
98
  - [x] `<ts>` DONE
100
99
  ```
101
- Set ticket Meta Status → `[x] DONE`.
100
+ Set ticket Meta Status → `[~] IN_PROGRESS`. **Not `[x] DONE` — the round is closed, not verified.** `DONE` is set in step 6, and only on audit PASS. Dependents pick on `DONE`, so setting it here advertises a task the audit has not seen yet.
102
101
 
103
102
  4a. **Sync Trackers** (MANDATORY, cannot skip):
104
103
 
105
- - Read `tasks/<scope>/README.md`. Find the Tracker row for this Task-ID. Set its Status → `[x] DONE`. Write back.
106
- - Read `tasks/README.md` Tracker Index. Update the scope's aggregate counts (done/total). Write back.
104
+ - Read `tasks/<scope>/README.md`. Find the Tracker row for this Task-ID. Set its Status to the ticket's current Meta Status. Write back.
105
+ - Read `tasks/README.md` Tracker Index. Update the scope's aggregate counts (done/total) — a task counts as done only at `[x] DONE`. Write back.
107
106
  - Verify: re-read both files, confirm the changes took effect. If not → retry once.
107
+ - Run this step again after step 6 sets the final status.
108
108
 
109
109
  5. **Dispatch AUDIT** (MANDATORY, always runs). Dispatch ONE subagent (`subagent_type: general-purpose`, **`model: "haiku"`** — audit is mechanical verification + fact-checking against artifacts; sonnet capability is overkill, haiku is faster and cheaper for this read-heavy task). Include in prompt the SDD tooling location: `~/Developer/gennady/ai/skills/sdd-execute/scripts/sdd` (audit may use `lint`, `verify`, `check-blockers` subcommands). With this prompt:
110
110
 
@@ -129,9 +129,9 @@ Pause path (distinguish from failure — skill is awaiting operator, not broken)
129
129
  Wait for return. If dispatch fails → retry once. If fails again → mark task FAILED.
130
130
 
131
131
  6. **Branch on audit status:**
132
- - `PASS` or `PASS_WITH_ACKNOWLEDGED_RISKS` → ticket verified; jump to step 9 (summary).
132
+ - `PASS` or `PASS_WITH_ACKNOWLEDGED_RISKS` → ticket verified. **Only now** set Meta Status → `[x] DONE` and re-run step 4a so the trackers and the aggregate counts follow. Jump to step 9 (summary).
133
133
  - `FAIL` AND `audit_attempt = 1` → step 7 (resolve findings).
134
- - `FAIL` AND `audit_attempt = 2` → STOP; jump to step 9 (summary with FAIL after 2 attempts).
134
+ - `FAIL` AND `audit_attempt = 2` → cap exhausted. Set Meta Status `[!] BLOCKED`, log `🛑 BLOCKED: audit-cap-exhausted` whose `💬 unblock:` line is the literal command `/sdd-execute <TSK-NN> --new-audit-session`, sync trackers, jump to step 9.
135
135
 
136
136
  7. **Resolve audit findings (max one retry, total 2 audit attempts):**
137
137
 
@@ -197,7 +197,8 @@ Pause path (distinguish from failure — skill is awaiting operator, not broken)
197
197
  - Writing code, audit reports, or phase blocks in Execution Log. (Subagents do.)
198
198
  - Skipping audit after all phases DONE. Audit dispatch is mandatory; this is the safety net.
199
199
  - Sharing context between phase subagents and audit subagent. Each gets a fresh prompt; orchestrator threads only typed Handoff payloads.
200
- - Audit retry beyond 2 total attempts. Hard cap.
200
+ - Audit retry beyond 2 attempts per session. Hard cap. Only the operator's literal `--new-audit-session` arg resets it — never your reading of «доделай» / «продолжай» / «finish it», which does not distinguish «lift the cap» from «finish what is already unblocked». No token → print the command and wait.
201
+ - Writing a `✅ RESOLVED` marker for a blocker that is not resolved. `check-blockers` counts markers, so one makes it dispatch — that is a bug you can trigger, not permission you can grant. The marker records a fact; fabricating it is the same class as a fabricated `ver` line.
201
202
  - Re-running phases not flagged in `phases_to_fix`. The map finding-location → phase is the contract; do not "just re-run everything".
202
203
  - Auto-reopening on phase BLOCKED/FAIL. Only on audit FAIL after all phases DONE the retry kicks in.
203
204
  - Parallel dispatch of phases of the SAME task. Phases are sequential by declared `Deps`. Cross-task parallelism is the job of `sdd-execute-batch`.
@@ -5,19 +5,37 @@
5
5
  # header presence, Task-ID integrity, or tracker sync. Pure function of files on disk.
6
6
  #
7
7
  # Three modes:
8
- # check.sh [project-root] — whole tree: TASKID + TRACKER_SYNC (all tickets) + HEADERS (all marker-bearing src)
9
- # check.sh --task <TSK-NN> [root] — one ticket: TASKID (collision/orphan-for-its-refs) + TRACKER_SYNC for that id
8
+ # check.sh [project-root] — whole tree: TASKID + TRACKER_SYNC (all tickets) + RULES (all rule files)
9
+ # check.sh --task <TSK-NN> [root] — one ticket: TASKID + TRACKER_SYNC for that id + RULES for its cited rules
10
10
  # check.sh --files <f1> [f2 ...] — header-trio presence for an explicit file list (audit passes its git-diff scope)
11
11
  #
12
12
  # Output sections (TSV, machine-readable, stable):
13
13
  # [HEADERS] — file \t has_file \t has_consumers \t has_tasks \t verdict(OK|PARTIAL|NONE)
14
14
  # [TASKID] — kind(orphan|collision) \t id \t detail
15
15
  # [TRACKER_SYNC] — task_id \t ticket_status \t tracker_status \t match(YES|NO|NO_ROW)
16
+ # [RULES] — file \t belief \t anti \t hooks \t reward \t verdict(OK|INCOMPLETE) \t missing
17
+ # [LOG] — ticket \t round \t line \t kind \t token \t detail
18
+ # kinds: unknown-token | unclosed-round (counted as findings)
19
+ # retired-token | round-close-no-timestamp (informational: append-only
20
+ # history and cosmetics never fail a tree)
16
21
  # [SUMMARY] — key=value totals + findings count
17
22
  #
23
+ # [RULES] implements the mechanical half of AX_RULES_COMPLIANCE_AGAINST_ACTIVATED_RULES: a rule file
24
+ # must expose <BeliefState> / <AntiPatterns> / <VerificationHooks> / <RewardCriteria>. Detection is a
25
+ # tolerant opening-tag scan, NOT an XML parse — these files are HTML-like by design and carry prose
26
+ # such as `<Target Files>` and `Meta<typeof Button>` that no XML parser accepts.
27
+ #
28
+ # Rule files are the non-`*.directive.xml` entries of the cascade categories (coding / testing / infra),
29
+ # in the project and in plugin directive trees. `*.directive.xml` are protocols, not rules, and are
30
+ # exempt. Tree mode scans every rule file; task mode scans only the ones that ticket's phases cite —
31
+ # the "activated" set the axiom is written against.
32
+ #
33
+ # Rule findings are counted SEPARATELY from task findings (`rule_findings=`): a shared rule file is
34
+ # project infrastructure that no single task owns or may edit, so it must not decide a task's verdict.
35
+ #
18
36
  # Exit codes:
19
37
  # 0 — all checks clean (zero findings)
20
- # 3 — one or more findings (desync / orphan / collision / partial-or-missing header)
38
+ # 3 — one or more findings (desync / orphan / collision / partial-or-missing header / incomplete rule)
21
39
  # 2 — structural failure (bad root / not an SDD project)
22
40
  # 4 — bad invocation
23
41
 
@@ -82,6 +100,7 @@ EOF
82
100
  esac
83
101
 
84
102
  FINDINGS=0
103
+ RULE_FINDINGS=0
85
104
 
86
105
  # ---------------------------------------------------------------------------
87
106
  # Mode: --files → HEADERS only
@@ -227,6 +246,148 @@ done <<< "$TASK_FILES"
227
246
  # is a policy (task-generated vs hand-authored), not a mechanical fact. Header presence
228
247
  # is meaningful only against a known in-scope file set — provided by audit via --files.
229
248
 
249
+ # ---------------------------------------------------------------------------
250
+ # [RULES] — activated rule files expose the four checkable sections
251
+ # ---------------------------------------------------------------------------
252
+
253
+ printf '\n[RULES]\n# file\tbelief\tanti\thooks\treward\tverdict\tmissing\n'
254
+
255
+ # Cascade categories only; `*.directive.xml` are protocols, not rules.
256
+ rule_files_in_tree() {
257
+ find -L "$ROOT_ABS/ai/directives" "$ROOT_ABS"/plugins/*/directives \
258
+ -type d -name node_modules -prune -o \
259
+ -type f -name '*.xml' ! -name '*.directive.xml' -print 2>/dev/null \
260
+ | grep -E '/(coding|testing|infra)/[^/]+\.xml$' | sort -u || true
261
+ }
262
+
263
+ # Task mode: the rules this ticket's phases actually cite (the "activated" set).
264
+ rule_files_for_task() {
265
+ local ticket
266
+ ticket=$(grep -l "^- \*\*Task-ID:\*\* $TASK_ID\$" $TASK_FILES 2>/dev/null | head -1)
267
+ [[ -z "$ticket" ]] && return
268
+ grep -ohE '(ai/directives|plugins/[a-z0-9-]+/directives)/[a-z0-9-]+/[a-z0-9._-]+\.xml' "$ticket" \
269
+ | grep -vE '\.directive\.xml$' | sort -u \
270
+ | while IFS= read -r rel; do
271
+ [[ -f "$ROOT_ABS/$rel" ]] && printf '%s\n' "$ROOT_ABS/$rel"
272
+ done
273
+ }
274
+
275
+ if [[ "$MODE" == "task" ]]; then
276
+ RULE_FILES=$(rule_files_for_task)
277
+ else
278
+ RULE_FILES=$(rule_files_in_tree)
279
+ fi
280
+
281
+ if [[ -z "$RULE_FILES" ]]; then
282
+ printf '# none%s\n' "$([[ "$MODE" == task ]] && echo " — ticket cites no rule files")"
283
+ else
284
+ while IFS= read -r rf; do
285
+ [[ -z "$rf" ]] && continue
286
+ b=0; a=0; h=0; r=0; missing=""
287
+ grep -q '<BeliefState' "$rf" && b=1 || missing="${missing}BeliefState,"
288
+ grep -q '<AntiPatterns' "$rf" && a=1 || missing="${missing}AntiPatterns,"
289
+ grep -q '<VerificationHooks' "$rf" && h=1 || missing="${missing}VerificationHooks,"
290
+ grep -q '<RewardCriteria' "$rf" && r=1 || missing="${missing}RewardCriteria,"
291
+ if [[ -z "$missing" ]]; then
292
+ printf '%s\t1\t1\t1\t1\tOK\t-\n' "${rf#$ROOT_ABS/}"
293
+ else
294
+ printf '%s\t%d\t%d\t%d\t%d\tINCOMPLETE\t%s\n' "${rf#$ROOT_ABS/}" "$b" "$a" "$h" "$r" "${missing%,}"
295
+ RULE_FINDINGS=$((RULE_FINDINGS+1))
296
+ fi
297
+ done <<< "$RULE_FILES"
298
+ fi
299
+
300
+ # ---------------------------------------------------------------------------
301
+ # [LOG] — Execution Log token vocabulary + Round-close shape
302
+ # ---------------------------------------------------------------------------
303
+
304
+ printf '\n[LOG]\n# ticket\tround\tline\tkind\ttoken\tdetail\n'
305
+
306
+ # `retired` tokens were valid before the vocabulary was consolidated. Rounds are append-only, so
307
+ # their presence in an old round is history, not a defect — they are reported and NOT counted.
308
+ # Anything outside both sets is `unknown-token` and IS counted.
309
+ LOG_TMP="$(mktemp -t sdd-check-log.XXXXXX)"
310
+ trap 'rm -f "$COLLISION_TMP" "$LOG_TMP"' EXIT
311
+
312
+ while IFS= read -r f; do
313
+ [[ -z "$f" ]] && continue
314
+ if [[ "$MODE" == "task" ]]; then
315
+ [[ "$(sdd_lib_task_id "$f")" == "$TASK_ID" ]] || continue
316
+ fi
317
+ awk -v ticket="${f#$ROOT_ABS/}" '
318
+ BEGIN {
319
+ split("intro decision tried discovery insight verified ver BLOCKED DONE", v, " ")
320
+ for (i in v) valid[v[i]] = 1
321
+ # Retired when the vocabulary was consolidated (scaffold.directive names exactly these).
322
+ split("sync file test cov rules recon", r, " ")
323
+ for (i in r) retired[r[i]] = 1
324
+ # Blocker lifecycle markers are their own shape, not action tokens.
325
+ valid["🛑"] = 1; valid["✅"] = 1
326
+ round = "-"; inlog = 0; inclose = 0; closelines = 0; closebad = 0
327
+ }
328
+ /^## 7\. Execution Log/ { inlog = 1; next }
329
+ /^## [0-9]+\./ { if (inlog) inlog = 0 }
330
+ inlog == 0 { next }
331
+ /^### Round / {
332
+ if (inclose && closelines != 1 && closebad == 0)
333
+ printf "%s\t%s\t%d\tbad-round-close\t-\texpected exactly one DONE line, found %d\n", ticket, round, closeline, closelines
334
+ round = $3; inclose = 0; closelines = 0; closebad = 0; next
335
+ }
336
+ /^#### Round close/ { inclose = 1; closelines = 0; closebad = 0; closeline = NR; next }
337
+ /^#### / {
338
+ if (inclose && closelines != 1 && closebad == 0)
339
+ printf "%s\t%s\t%d\tbad-round-close\t-\texpected exactly one DONE line, found %d\n", ticket, round, closeline, closelines
340
+ inclose = 0; closebad = 0; next
341
+ }
342
+ # Token lines: "- [x] `<ts>` <token> ..." (blocker lines use 🛑 and are matched separately)
343
+ /^- \[[ x~!]\] `[^`]*` / {
344
+ rest = $0; sub(/^- \[[ x~!]\] `[^`]*` +/, "", rest)
345
+ split(rest, w, " "); tok = w[1]
346
+ sub(/:$/, "", tok) # a trailing colon is cosmetic, not a different token
347
+ if (inclose) { if (tok == "DONE") closelines++ }
348
+ if (tok in valid) next
349
+ # Every real token is lowercase except BLOCKED / DONE. A capitalised first word is a
350
+ # sentence from the pre-consolidation prose plan ("Implementation file:", "Tracker
351
+ # synced:"), which the same consolidation retired.
352
+ if (tok ~ /^[A-Z]/) {
353
+ printf "%s\t%s\t%d\tretired-token\t%s\tprose line from the pre-consolidation plan template\n", ticket, round, NR, tok
354
+ next
355
+ }
356
+ if (tok in retired) {
357
+ printf "%s\t%s\t%d\tretired-token\t%s\tvalid before the vocabulary was consolidated; round is append-only\n", ticket, round, NR, tok
358
+ } else {
359
+ printf "%s\t%s\t%d\tunknown-token\t%s\tnot in the scaffold.directive token table\n", ticket, round, NR, tok
360
+ }
361
+ next
362
+ }
363
+ # A checkbox line inside Round close that carries no timestamped token at all.
364
+ # Close-block lines that carry no timestamped token. A ticked DONE without a timestamp is
365
+ # cosmetic drift; an unticked box means the round was never actually closed.
366
+ inclose == 1 && /^- / {
367
+ if ($0 ~ /\[x\]/ && $0 ~ /DONE/) {
368
+ printf "%s\t%s\t%d\tround-close-no-timestamp\tDONE\tclosed, but the `<ts>` is missing\n", ticket, round, NR
369
+ closelines++
370
+ } else {
371
+ printf "%s\t%s\t%d\tunclosed-round\t-\tRound close carries no ticked DONE: %s\n", ticket, round, NR, substr($0, 1, 40)
372
+ closebad = 1
373
+ }
374
+ }
375
+ END {
376
+ if (inclose && closelines != 1 && closebad == 0)
377
+ printf "%s\t%s\t%d\tbad-round-close\t-\texpected exactly one DONE line, found %d\n", ticket, round, closeline, closelines
378
+ }
379
+ ' "$f" >> "$LOG_TMP"
380
+ done <<< "$TASK_FILES"
381
+
382
+ if [[ -s "$LOG_TMP" ]]; then
383
+ cat "$LOG_TMP"
384
+ # Informational kinds record history or cosmetics; they must not fail the tree.
385
+ LOG_FINDINGS=$(awk -F'\t' '$4 != "retired-token" && $4 != "round-close-no-timestamp"' "$LOG_TMP" | grep -c '' || true)
386
+ FINDINGS=$((FINDINGS + LOG_FINDINGS))
387
+ else
388
+ printf '# none\n'
389
+ fi
390
+
230
391
  # ---------------------------------------------------------------------------
231
392
  # [SUMMARY]
232
393
  # ---------------------------------------------------------------------------
@@ -234,5 +395,6 @@ done <<< "$TASK_FILES"
234
395
  printf '\n[SUMMARY]\nmode=%s\n' "$MODE"
235
396
  [[ "$MODE" == "task" ]] && printf 'task=%s\n' "$TASK_ID"
236
397
  printf 'findings=%d\n' "$FINDINGS"
398
+ printf 'rule_findings=%d\n' "$RULE_FINDINGS"
237
399
 
238
- [[ "$FINDINGS" -gt 0 ]] && exit 3 || exit 0
400
+ [[ $((FINDINGS + RULE_FINDINGS)) -gt 0 ]] && exit 3 || exit 0
@@ -4,7 +4,10 @@
4
4
  # @contract: AX_BASH_NO_SILENT_EMPTY — never produces empty stdout. On miss → actionable instruction.
5
5
  #
6
6
  # Why this wrapper exists:
7
- # - gennady CLI lives outside the project (canonical: ~/Developer/gennady/cli/gennady.ts).
7
+ # - gennady CLI may live outside the project; resolution is done at RUNTIME (see resolve_gennady).
8
+ # It MUST NOT be a bare dev-path literal assigned to a variable: `sync-skills` rewrites dev paths
9
+ # through PathNormalizer, which turned that assignment into an unquoted two-word command, leaving
10
+ # the variable unset — every deployed copy then died on `set -u`. Keep this file path-literal-free.
8
11
  # - gennady requires `node --experimental-strip-types` (Node 22+) — the bare `node` invocation is non-obvious.
9
12
  # - gennady returns exit code 0 even when lint reports errors (the failure signal is the literal token
10
13
  # "[linting → failed]" in stdout). We must parse output, not trust exit code.
@@ -23,7 +26,29 @@
23
26
  set -uo pipefail
24
27
 
25
28
  PROG="lint-artifacts"
26
- GENNADY_CLI=~/Developer/gennady/cli/gennady.ts
29
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
30
+
31
+ # Repo root of a gennady checkout: scripts → sdd-execute → skills → ai → root.
32
+ GENNADY_HOME="${GENNADY_HOME:-$SCRIPT_DIR/../../../..}"
33
+
34
+ # Runtime resolution — deliberately contains no dev-path literal, so PathNormalizer has
35
+ # nothing to rewrite and the deployed copy behaves identically to the checkout copy.
36
+ GENNADY_ARGV=()
37
+ resolve_gennady() {
38
+ if command -v gennady &>/dev/null; then
39
+ GENNADY_ARGV=(gennady)
40
+ return 0
41
+ fi
42
+ if [[ -x "$GENNADY_HOME/node_modules/.bin/tsx" && -f "$GENNADY_HOME/cli/gennady.ts" ]]; then
43
+ GENNADY_ARGV=("$GENNADY_HOME/node_modules/.bin/tsx" "$GENNADY_HOME/cli/gennady.ts")
44
+ return 0
45
+ fi
46
+ if [[ -x "./node_modules/.bin/gennady" ]]; then
47
+ GENNADY_ARGV=(./node_modules/.bin/gennady)
48
+ return 0
49
+ fi
50
+ return 1
51
+ }
27
52
 
28
53
  if [[ $# -lt 1 ]]; then
29
54
  cat <<EOF
@@ -38,16 +63,18 @@ EOF
38
63
  exit 4
39
64
  fi
40
65
 
41
- if [[ ! -f "$GENNADY_CLI" ]]; then
66
+ if ! resolve_gennady; then
42
67
  cat <<EOF
43
68
  [$PROG] GENNADY_CLI_NOT_FOUND
44
- expected at: $GENNADY_CLI
69
+ tried: gennady on PATH
70
+ \$GENNADY_HOME/cli/gennady.ts via checkout tsx (GENNADY_HOME=$GENNADY_HOME)
71
+ ./node_modules/.bin/gennady
45
72
 
46
73
  Diagnosis: the gennady AST DbC linter is unreachable from this environment.
47
74
 
48
75
  Required action (ORCHESTRATOR):
49
- 1. Verify the gennady project is checked out at ~/Developer/gennady.
50
- 2. If gennady moved → update GENNADY_CLI variable in this script.
76
+ 1. Install it in the project (\`npm i -D gennady\`) or put it on PATH.
77
+ 2. Working from a gennady checkout → export GENNADY_HOME=<checkout-root>.
51
78
  3. If on a CI/sandbox without gennady → this is a HARD blocker; phase cannot verify.
52
79
  Report to operator: cannot complete phase without DBC contract verification.
53
80
 
@@ -65,7 +92,7 @@ tmp_out=$(mktemp -t lint-artifacts.XXXXXX)
65
92
  trap 'rm -f "$tmp_out"' EXIT
66
93
 
67
94
  # Run gennady. We intentionally ignore its exit code (unreliable per contract above).
68
- npx tsx "$GENNADY_CLI" lint "$@" > "$tmp_out" 2>&1 || true
95
+ "${GENNADY_ARGV[@]}" lint "$@" > "$tmp_out" 2>&1 || true
69
96
 
70
97
  has_clean=$(grep -c '\[linting → clean\]' "$tmp_out" 2>/dev/null || echo 0)
71
98
  has_failed=$(grep -c '\[linting → failed\]' "$tmp_out" 2>/dev/null || echo 0)
@@ -108,7 +135,7 @@ Required action (PHASE AGENT, before EMIT_HANDOFF):
108
135
  5. Do NOT EMIT_HANDOFF with lint failures present — that is fabricated DONE.
109
136
 
110
137
  References:
111
- /Users/k.lebedev/Developer/vkt/ai/directives/coding/typescript-rules.xml
138
+ ai/directives/coding/typescript-rules.xml
112
139
  — AX_TAG_USAGE_MATRIX, AX_BASE_CONTRACT_SHAPE, AX_FLAT_JSDOC_FOR_PROPERTIES
113
140
  EOF
114
141
  exit 2
@@ -135,7 +162,7 @@ Captured output:
135
162
  $(cat "$tmp_out" | head -50)
136
163
 
137
164
  Required action (ORCHESTRATOR):
138
- 1. Re-read gennady source: ~/Developer/gennady/cli/gennady.ts
165
+ 1. Re-read the gennady LintCommand source in the checkout you resolved above.
139
166
  2. Check command output tokens in the LintCommand implementation.
140
167
  3. Update this script's parsing to match new tokens.
141
168
  4. Until resolved → treat as a HARD blocker; DO NOT assume PASS.
@@ -174,7 +174,7 @@ run_cmd() {
174
174
  while IFS=: read cls name; do
175
175
  case "$cls" in
176
176
  typecheck) run_cmd "typecheck" "npm run $name" npm run "$name" || true ;;
177
- gennady) run_cmd "gennady DBC lint" "gennady lint ${#FILES[@]} files" npx tsx ~/Developer/gennady/cli/gennady.ts lint "${FILES[@]}" || true ;;
177
+ gennady) run_cmd "gennady DBC lint" "gennady lint ${#FILES[@]} files" "$SCRIPT_DIR/lint-artifacts.sh" "${FILES[@]}" || true ;;
178
178
  lint) run_cmd "lint" "npm run $name" npm run "$name" || true ;;
179
179
  test) run_cmd "test" "npm run $name" npm run "$name" || true ;;
180
180
  format) run_cmd "format check" "npm run $name" npm run "$name" || true ;;
@@ -125,12 +125,13 @@ Per-task phase tokens:
125
125
  ```
126
126
 
127
127
  b. Branch on phase status:
128
- - `BLOCKED` or `FAIL` → STOP this task's lane; mark task FAILED for the batch. Other parallel tasks in same sub-batch continue.
128
+ - `BLOCKED` on a repo-wide gate failing in files this ticket does not own → PARK the lane, do not fail it. Parallel lanes share one working tree, so that is usually a sibling lane mid-flight. Resume each parked lane once after the sub-batch drains; still blocked → `✋ AWAITING UNBLOCK`, not `❌ FAILED`.
129
+ - `BLOCKED` or `FAIL` otherwise → STOP this task's lane; mark task FAILED for the batch. Other parallel tasks in same sub-batch continue.
129
130
  - `DONE` → record Handoff (artifacts, decisions, open). Continue to next phase.
130
131
 
131
132
  c. Thread next phase's Inputs from this phase's Handoff (verbatim).
132
133
 
133
- After all phases DONE: close Round (append `#### Round close` block: sync + DONE). Sync trackers. Ticket Status → `[x] DONE`.
134
+ After all phases DONE: close Round (append `#### Round close` block per `ROUND_CLOSE_FORMAT`: a single `DONE` line). Ticket Status → `[~] IN_PROGRESS` — closed, not verified. Sync trackers to that status. `[x] DONE` is set in step e, on audit PASS only: dependents pick on `DONE`, and a later layer must not start against a task the audit has not seen.
134
135
 
135
136
  d. Dispatch AUDIT subagent (`subagent_type: general-purpose`, **`model: "haiku"`** — audit is mechanical verification + fact-checking, haiku sufficient and cheaper). MANDATORY, always runs. Include in prompt the SDD tooling location: `~/Developer/gennady/ai/skills/sdd-execute/scripts/sdd` (audit may use `lint`, `verify`, `check-blockers` subcommands):
136
137
  ```
@@ -153,15 +154,15 @@ Per-task phase tokens:
153
154
  Wait for return. If dispatch fails → retry once. If fails again → mark task FAILED.
154
155
 
155
156
  e. Branch on audit status:
156
- - `PASS` / `PASS_WITH_ACKNOWLEDGED_RISKS` → task complete; continue lane (next task in batch).
157
+ - `PASS` / `PASS_WITH_ACKNOWLEDGED_RISKS` → task verified. Only now set Ticket Status → `[x] DONE` and re-sync trackers. Continue lane (next task in batch).
157
158
  - `FAIL` AND `audit_attempt = 1` → trigger selective phase re-run.
158
159
  Open new Round with reason `audit-driven fix: F-NNN, F-MMM`.
159
160
  For each phase in `phases_to_fix` (sequential, declared order subset):
160
161
  Dispatch PHASE subagent with `Reason: fix: address audit findings F-NNN, F-MMM` and audit findings list in Inputs. Wait.
161
162
  On BLOCKED/FAIL → STOP this task's lane; mark task FAILED.
162
163
  After all fix phases DONE → close Round → dispatch AUDIT (round 2, fresh context). Branch again:
163
- PASS → task complete.
164
- FAIL (audit_attempt = 2) → STOP lane; mark task FAILED.
164
+ PASS → task verified: Ticket Status → `[x] DONE`, re-sync trackers.
165
+ FAIL (audit_attempt = 2) → STOP lane; cap exhausted. Meta Status `[!] BLOCKED`, `🛑 BLOCKED: audit-cap-exhausted` with `💬 unblock: /sdd-execute <TSK-NN> --new-audit-session`. Report as `🛑 cap-exhausted`, distinct from `❌ FAILED`. The batch never lifts the cap itself.
165
166
 
166
167
  — Wait for all parallel task lanes in sub-batch to finish.
167
168
  — Any task FAILED → continue batch (other layers may not depend on it; if they do they'll be marked `⏸️ waiting`). Operator gets failure list in final summary.
@@ -202,7 +203,8 @@ Per-task phase tokens:
202
203
  - Writing code or phase blocks in Execution Log. (Phase subagents do.)
203
204
  - Sharing context between phase subagents of different tasks. Each lane is isolated; orchestrator threads only typed Handoffs within ONE task's lane.
204
205
  - Skipping audit after Round close in default (per-task) mode. Audit dispatch is mandatory.
205
- - Audit retry beyond 2 total attempts per task. Hard cap.
206
+ - Audit retry beyond 2 attempts per task per session. Hard cap; only the operator's literal `--new-audit-session` arg resets it, and only via the sibling `sdd-execute` skill.
207
+ - Writing a `✅ RESOLVED` marker for a blocker that is not resolved, to get past the `check-blockers` preflight. The marker records a fact, not permission.
206
208
  - Re-running phases not flagged in `phases_to_fix`. The map finding-location → phase is the contract.
207
209
  - Parallel dispatch ACROSS layers. Layers run sequentially.
208
210
  - Parallel dispatch ACROSS sub-batches in same layer. Sub-batches exist exactly because of file conflicts.