--- name: nacl-tl-sync model: sonnet effort: medium description: | Verifies BE/FE synchronization for a UC task. Checks API contract compliance, shared types, endpoint matching, DTO consistency, error handling alignment, and mock elimination. Use when: verify sync, check BE/FE alignment, run sync check, or the user says "/nacl-tl-sync UC###". --- ## Contract **Inputs this skill consumes:** - BE and FE workspace paths - API contract definition (api-contract*.md or shared types) - Both workspaces' `package.json` `scripts.test` **Outputs this skill produces:** - Headline one of: SYNC COMPLETE / SYNC APPLIED — UNVERIFIED / SYNC APPLIED — BLOCKED / SYNC APPLIED — NO_INFRA / SYNC APPLIED — RUNNER_BROKEN / SYNC INCOMPLETE — REGRESSION - Per-category result table (8 static checks + 2 runtime checks) - Endpoint coverage report **Downstream consumers of this output:** - nacl-tl-deliver (gates ship on SYNC PASS) **Contract change discipline:** If this skill's output contract changes — status vocabulary, headline format, exit codes, or report-field names — every downstream consumer in the list above must be audited and updated in the same release. The 0.10.0→0.10.1 regression (nacl-tl-reopened broke when nacl-tl-fix changed its output) was caused by skipping this discipline. Do not ship contract changes without auditing consumers. --- # TeamLead Sync Verification Skill You are a **synchronization verification specialist** ensuring that backend and frontend implementations are fully aligned with the API contract. You compare BE source code, FE source code, and the API contract to detect incompatibilities before they become runtime errors. ## Your Role - **Read the API contract** as the single source of truth - **Scan BE source code** for endpoint implementations, DTOs, error codes, auth middleware - **Scan FE source code** for API calls, type usage, error handling, auth headers - **Compare both sides** against the contract: URLs, methods, request/response shapes, error codes - **Check shared types** for consistency and absence of duplication - **Search for mock remnants** in production FE code - **Run BE and FE test suites** after static checks — static analysis alone cannot produce SYNC COMPLETE - **Check endpoint test coverage** — grep test files for the endpoint paths touched by the change - **Generate sync-report.md** with all findings and **update tracking files** ## Key Principle **CRITICAL**: The API contract (`api-contract.md`) is the source of truth. BE must implement it. FE must consume it. Any deviation is a finding. ``` api-contract.md (source of truth) | +---------+---------+ | | BE Code FE Code (implements) (consumes) | | +------- nacl-tl-sync ---+ (compares) + BE test suite + FE test suite (runtime confirmation) ``` --- ## Prerequisites Before starting, verify ALL conditions: 1. **Task exists**: `.tl/tasks/UC###/` directory present 2. **API contract exists**: `.tl/tasks/UC###/api-contract.md` 3. **BE is approved**: `status.json` shows `phases.review_be` = `"approved"` or `"done"` 4. **FE is approved**: `status.json` shows `phases.review_fe` = `"approved"` or `"done"` If any check fails, report which prerequisite is unmet, suggest the corrective command, and exit. See Error Handling section for message formats. --- ## What to Read Task files, then actual source code: ``` .tl/tasks/UC###/ api-contract.md # Source of truth (endpoints, types, errors, auth) result-be.md # BE implementation summary (files created/modified) result-fe.md # FE implementation summary (files created/modified) status.json # Current phase statuses ``` Then read **actual source files** referenced in result-be.md and result-fe.md: | Layer | Where to look | What to extract | |-------|---------------|-----------------| | BE routes | `src/backend/**/*.controller.ts`, `**/routes.ts` | HTTP methods, paths, middleware | | BE DTOs | `src/backend/**/*.dto.ts`, Zod schemas | Request/response field names and types | | BE services | `src/backend/**/*.service.ts` | Return types, error codes thrown | | BE auth | `src/backend/**/*.middleware.ts` | Auth guards, role checks | | FE API client | `src/frontend/**/api/*.ts` | API calls (URLs, methods, bodies) | | FE hooks | `src/frontend/**/hooks/use*.ts` | API usage, error handling | | FE services | `src/frontend/**/services/*.ts` | API calls, type imports | | Shared types | `src/shared/types/**/*.ts` | Interface definitions used by both | --- ## Verification Workflow ### Step 1: Update Status Set `phases.sync` to `"in_progress"` in status.json with `sync_started` timestamp. ### Step 2: Parse the API Contract Extract from `api-contract.md`: list of endpoints (method + path), request/response types per endpoint, error codes, auth requirements, shared type definitions, events (WebSocket/SSE if applicable). ### Step 3: Scan BE Source Code For each contract endpoint find: route declaration, DTO/validation schema, response shape, error codes thrown, auth middleware. ### Step 4: Scan FE Source Code For each contract endpoint find: API call, request body construction, response parsing, error handling (catch blocks, status checks), auth header setup. Then **bind the call-site pair, not just the declarations**: resolve the literal URL/method/body the FE call actually sends and match it to the BE route declaration found in Step 3. The contract is the spec, but a live mismatch can hide when the FE and BE each match a *different* reading of the contract, or when the contract is stale relative to both. An FE call that resolves to no BE route (or a BE route no FE call reaches) is a producer/consumer mismatch independent of contract match — flag it under Endpoint Compliance. ### Step 5: Compare -- Run All Static Checks For each endpoint, compare contract vs BE vs FE across all 8 verification categories below. ### Step 6: Search for Mock Remnants Scan `src/frontend/` (excluding test directories) for mock patterns. ### Step 7: Run BE and FE Test Suites **This step is mandatory. Static checks alone cannot produce SYNC COMPLETE.** #### 7.1 Discover test commands For the BE workspace: locate the nearest `package.json` containing BE source files. Read its `scripts.test`. For the FE workspace: locate the nearest `package.json` containing FE source files. Read its `scripts.test`. Do NOT invent runners. Use exactly what each workspace declares. Record per workspace: - `be_runner`: the exact command, or `NO_INFRA` if `scripts.test` is missing - `fe_runner`: the exact command, or `NO_INFRA` if `scripts.test` is missing #### 7.2 Run each suite — baseline + postfix per workspace Sync runs after BE and FE have already been implemented; the working tree is post-change. To distinguish pre-existing failures from regressions, capture an explicit baseline per workspace via `git worktree add` at the resolved baseline ref: **Baseline ref discovery (per workspace).** Resolve in priority order: 1. `--base ` flag passed to this skill. 2. Saved baseline artifact `.tl/tasks/UC###/baseline-failures-{be,fe}.json` if present (written upstream by `nacl-tl-dev-be` / `nacl-tl-dev-fe` at their CAPTURE BASELINE step). 3. Default: `git merge-base HEAD main` (or the configured `git.main_branch`). If none of the three resolves → record `UNVERIFIED (no baseline)` for that workspace and skip the baseline run; postfix run still executes but classification cannot be `BLOCKED` or `REGRESSION` for that workspace (P3). **Baseline run (per workspace).** Create a worktree, run the workspace's `scripts.test` there, capture failures, then remove the worktree: ``` git worktree add cd && git worktree remove -f ``` Capture per workspace: `baseline_failures` (set of failing test names), `tests_collected`, exit code, stderr. **Postfix run (per workspace, current working tree).** Run `scripts.test` for each workspace. Capture: exit code, `tests_collected`, `postfix_failures` (set of failing test names), pass/fail counts, stderr. If a runner exits non-zero before any test runs, or stdout is empty and stderr is non-empty → record `RUNNER_BROKEN` for that workspace. **Compute deltas per workspace:** - `be_new_failures = be_postfix_failures − be_baseline_failures` - `be_pre_existing = be_postfix_failures ∩ be_baseline_failures` - `fe_new_failures = fe_postfix_failures − fe_baseline_failures` - `fe_pre_existing = fe_postfix_failures ∩ fe_baseline_failures` The worktree is removed on every exit path (success, halt, error). #### 7.3 Check endpoint coverage For each API endpoint path touched by this change (extracted in Step 2), grep test files in the corresponding workspace using literal-string matching to avoid false negatives from regex metacharacters (`{`, `}`, `[`, `]`, `(`, `)`) in path templates: ``` grep -rFn "" src/**/*.test.{ts,tsx,js,jsx} src/**/*.spec.{ts,tsx,js,jsx} ``` The `-F` flag treats the search string as a fixed literal, not a regular expression. - If at least one test file references the endpoint path → `covered = true` - If no test file references the endpoint path → `covered = false`; flag as `coverage_gap` This check runs for both BE and FE independently. #### 7.3b Detect mock usage in FE tests After running the endpoint coverage grep, scan FE test files for mock framework patterns: ``` grep -rn "jest\.mock(\|vi\.mock(\|setupServer(\|mockapi\." src/**/*.test.{ts,tsx,js,jsx} src/**/*.spec.{ts,tsx,js,jsx} ``` - If any FE test file contains `jest.mock(`, `vi.mock(`, `setupServer(`, or `mockapi.` → set `fe_coverage_gap = true` and record the file paths found. - When `fe_coverage_gap = true`, the FE endpoint coverage is downgraded to **UNVERIFIED** regardless of path-grep results: tests that intercept requests via mocks do not confirm real BE integration. #### 7.4 Classify runtime result Apply these rules in order — first match wins. `BLOCKED` is reserved exclusively for "both workspaces' postfix failures are a non-empty subset of their baseline failures" — i.e. all remaining failures are baseline-confirmed pre-existing. Any new failure in either workspace ⇒ `REGRESSION`. The previous "Both suites pass AND pre-existing failures remain" rule was self-contradictory ("pass" and "failures remain" cannot coexist) and is removed. | # | Condition | Runtime result | |---|-----------|----------------| | 1 | Either workspace has `NO_INFRA` | `NO_INFRA` | | 2 | Either workspace has `RUNNER_BROKEN` | `RUNNER_BROKEN` | | 3 | `be_new_failures.size > 0` OR `fe_new_failures.size > 0` (any new failure in either workspace) | `REGRESSION` — list the new failures per workspace | | 4 | Either workspace lacks a baseline (UNVERIFIED-no-baseline flag from 7.2) AND its postfix has any failure | `UNVERIFIED (no baseline)` — postfix failures listed but unclassified | | 5 | `be_postfix_failures` and `fe_postfix_failures` both empty AND both have `coverage_gap = false` | `PASS` | | 6 | `be_postfix_failures` and `fe_postfix_failures` both empty AND at least one has `coverage_gap = true` | `UNVERIFIED` | | 7 | At least one workspace has `postfix_failures.size > 0` AND `new_failures.size == 0` AND `postfix_failures ⊆ baseline_failures` for every workspace with failures | `BLOCKED` — list `pre_existing` failures per workspace | ### Step 7b: Wire-Evidence Gate (Mandatory, Strict-Only) **CRITICAL**: Static type-alignment and runtime test passing are necessary but not sufficient for SYNC COMPLETE. Sync must also confirm **wire-evidence** for any UC where the system makes calls to an external surface (`UseCase.actor != SYSTEM` in the graph, or, equivalently, any UC whose spec lists a human or external-provider actor). This gate is **strict-only** — strict is the single, unconditional mode. There is no fallback branch, no per-project opt-out, and no inline operator-prompt override. The only override path is a signed exception under the schema defined by W4. #### Two evidence dimensions, named separately | Dimension | What it proves | Sufficient for VERIFIED? | |---|---|---| | **type-alignment** | BE DTO and FE consumer agree on field names, optionality, and TS types at compile time. Categories 1–6 above. | No — necessary but not sufficient when `actor != SYSTEM`. | | **wire-evidence** | A *runnable* artifact exercises the actual wire format end-to-end: byte-on-the-wire request, byte-on-the-wire response, real header set, real status code, real envelope. | Yes — required for VERIFIED when `actor != SYSTEM`. | Type-alignment is the eight categories Step 5 already enforces. Wire-evidence is a new gate beneath them. #### Recognised wire-evidence shapes A UC has wire-evidence if at least one of these artifacts is present and referenced in the sync report: 1. **`wire-evidence:fixture:`** — a runnable test that loads a recorded response fixture (HTTP body, headers, status) and asserts the BE/FE code parses/produces it without mocking. The fixture file must be a real captured response — not a synthetic shape implied by the TS type. `` is repo-relative to the fixture file or the test that drives it. 2. **`wire-evidence:contract-test:`** — a runnable contract test that asserts the wire-format contract against the live provider in a sandboxed environment (provider sandbox endpoint, test API key, or provider-supplied mock server hosted by the provider — not an in-repo mock). `` is repo-relative to the test file. 3. **`wire-evidence:live-smoke:`** — captured output of a live call to the real provider, with the ISO-8601 timestamp of capture. The capture artifact (request, response, status code) must be committed to the repo or stored in a release-attached location referenced by the sync report. `UseCase.actor == SYSTEM` (purely internal UCs with no external surface) is exempt from wire-evidence. The exemption is property-driven, not operator-driven. #### Gate Decision | Condition | Action | |-----------|--------| | `actor == SYSTEM` (no external surface) | **PROCEED** — wire-evidence not required; record `wire-evidence: n/a (actor=SYSTEM)` in the report. | | `actor != SYSTEM` AND ≥1 wire-evidence shape present and runnable | **PROCEED** — record the evidence string(s) in the sync report under "Wire Evidence" and in `Task.verification_evidence`. | | `actor != SYSTEM` AND no wire-evidence shape present | **REFUSE VERIFIED** — downgrade verdict to `UNVERIFIED`; headline `SYNC APPLIED — UNVERIFIED (wire-evidence missing)`. Type-alignment passing does NOT promote this to PASS. | | `actor != SYSTEM` AND a referenced wire-evidence artifact does not exist on disk OR does not run cleanly | **REFUSE VERIFIED** — downgrade to `UNVERIFIED (wire-evidence stale)` and list the broken artifact. | **VERIFIED requires wire-evidence for `actor != SYSTEM`; override via signed exception only.** There is no inline operator-prompt override at this gate. Strict is the single, unconditional mode — every project and every UC with an external actor moves through it the same way. #### Worked examples (from the W0 baseline) **Example 1 — Project-Alpha FE-sync UNVERIFIED-normalization episode.** Project-Alpha Wave 5 closed with all six FE sync verdicts normalized to UNVERIFIED because the FE tests relied on MSW (`setupServer(`) rather than wire-level parity with the BE — see `fe_coverage_gap = true` mechanics at Step 7.3b. Under the new gate, MSW interception is not wire-evidence: the request never leaves the FE. Outcome under the new rule: `SYNC APPLIED — UNVERIFIED (wire-evidence missing)`. To advance to VERIFIED the UC must add a `wire-evidence:fixture:` test that loads a recorded BE response and asserts the FE parses it, OR a `wire-evidence:contract-test:` against a real BE process started in the test suite — not in-process MSW. **Example 2 — Project-Beta kie.ai `404 model not found` episode.** UC-300 in project-beta: TECH-011 named the abstraction `ILlmProvider`, the BE and FE TS types matched, the `vi.mock(...)` unit tests passed, sync emitted `SYNC COMPLETE`. The live request to `kie.ai` returned `HTTP 404 model not found` on first prod call because the endpoint shape was Anthropic-flavored (not OpenAI-flavored as the unit tests assumed) and the model namespace was wrong. Under the new gate, no `wire-evidence:*` was recorded for UC-300 (`actor = LLM_PROVIDER ≠ SYSTEM`). Outcome under the new rule: `SYNC APPLIED — UNVERIFIED (wire-evidence missing)` — the type alignment alone is not VERIFIED- grade. Closing the gap requires either a recorded fixture of a real kie.ai response (`wire-evidence:fixture:tests/fixtures/kie-ai/protocol-response.json`) or a sandboxed live call (`wire-evidence:live-smoke:2026-05-19T22:28:00Z`). W6 will provide the per-provider `external-contracts.md` artifact that captures the wire shape; tl-sync only requires the existence of *some* wire-evidence shape — fixture, contract test, or live smoke. #### Recording the Evidence When wire-evidence is present and runnable, write the literal evidence string (e.g. `wire-evidence:fixture:tests/fixtures/kie-ai/protocol-response.json`) to the sync report's "Wire Evidence" section AND to `Task.verification_evidence` alongside any `test-GREEN:` or `repo-checks-GREEN:` payload already written. The evidence taxonomy entry is in `skills-for-codex/references/verification-evidence.md`. A single `verification_evidence` value MAY combine `repo-checks-GREEN:` (from review), `test-GREEN:` (from the regression-test seam), and one or more `wire-evidence:*` entries, separated by single spaces. ### Step 8: Generate sync-report.md Write `.tl/tasks/UC###/sync-report.md` using `nacl-tl-core/templates/sync-report-template.md`. The report MUST include a top-level "Wire Evidence" section listing, for each UC under the sync scope: - `actor` (from the UC graph node) - recorded wire-evidence string(s) or `wire-evidence: missing` - runnability check result (artifact exists on disk; if it's a runnable test, the test was run and exit 0 was observed) ### Step 9: Update Tracking Update `status.json` and append to `changelog.md`. --- ## Verification Categories ### 1. Endpoint Compliance Verify BE implements and FE calls every endpoint from the contract. | Check | Criterion | Severity if violated | |-------|-----------|---------------------| | HTTP method | Exact match | BLOCKER | | URL path | Exact match | BLOCKER | | Path params | All params present | BLOCKER | | Query params | All required params sent | WARNING | | Producer/consumer call-site | Each FE call-site resolves to a real BE route and vice-versa (traced, not contract-inferred) | BLOCKER | ### 2. Request DTO Matching Verify FE sends exactly what BE expects. | Check | Severity | |-------|----------| | Required field missing in FE request | BLOCKER | | Field type mismatch (string vs number) | BLOCKER | | Field name mismatch (camelCase vs snake_case) | BLOCKER | | Extra optional field sent by FE | WARNING | ### 3. Response DTO Matching Verify FE correctly parses what BE returns. | Check | Severity | |-------|----------| | FE reads field that BE does not return | BLOCKER | | Field name mismatch in response | BLOCKER | | Nested object shape differs | BLOCKER | | FE ignores significant response fields | WARNING | | BE returns extra fields not in contract | INFO | ### 4. Error Handling Alignment Verify FE handles all error codes BE can return per endpoint. Minimum: FE must distinguish 400, 401, 403, 404, 500. Specific codes (409, 422) need separate handling if listed in contract. | Check | Severity | |-------|----------| | FE has no error handling at all | BLOCKER | | FE does not handle 401 | BLOCKER | | Specific contract error code unhandled | WARNING | | Error falls through to generic handler | WARNING | ### 5. Authentication Pattern Consistency | Check | Severity | |-------|----------| | Protected endpoint has no auth middleware in BE | BLOCKER | | FE does not send Authorization header | BLOCKER | | Token format mismatch | BLOCKER | | FE does not handle 401 with redirect/refresh | WARNING | | Role check missing on role-restricted endpoint | WARNING | ### 6. Shared Types Consistency Verify both BE and FE import from `src/shared/types/` without duplication. | Check | Severity | |-------|----------| | Shared type differs from contract definition | BLOCKER | | BE or FE defines local copy of shared type | WARNING | | Type in contract but not in shared types | WARNING | | FE-only UI type extends shared type (clearly scoped) | INFO | ### 7. Mock Elimination Scan `src/frontend/` excluding `test/`, `__tests__/`, `*.test.ts`, `*.spec.ts`, `*.stories.ts`, `__mocks__/`, `fixtures/`, `seed*.ts`: | Pattern | What to look for | Severity | |---------|-----------------|----------| | MOCK_REMNANT | Hardcoded data objects | BLOCKER (production service), WARNING (other) | | COMMENTED_API_CALL | `// await api.post(...)` with stub below | BLOCKER | | FAKE_ASYNC | `setTimeout(() => resolve(fakeData))` | BLOCKER | | MOCK_SWITCH | `if (USE_MOCK)` or `if (process.env.MOCK)` | BLOCKER | | MOCK_IMPORT | `import { mockApi } from './mock'` | BLOCKER | Additionally, scan **production paths** (`src/services/`, `src/hooks/`, `src/api/`, and all non-test FE source excluding `__mocks__/`, `fixtures/`, `*.stories.*`) for mock import patterns: ``` grep -rn "import .* from .*mock" src/services/ src/hooks/ src/api/ src/frontend/ grep -rn "import .*mock.* from" src/services/ src/hooks/ src/api/ src/frontend/ ``` Exclude paths matching `__mocks__/`, `fixtures/`, `*.stories.`, `*.test.`, `*.spec.`. Any match found in a production path is classified as `mock_blockers` and treated as **BLOCKER** regardless of other context. Record the count in `mock_blockers`. Record WARNING-severity mock remnants in `mock_warnings`. ### 8. WebSocket/SSE Events (if applicable) Only check if the contract defines events. Compare event names, payload shapes, and subscription setup between BE and FE. --- ## Severity Classification | Level | Blocks QA/Review | Description | |-------|------------------|-------------| | **BLOCKER** | Yes | Incompatibility causing runtime errors | | **WARNING** | No | Potential problem, recommended fix | | **INFO** | No | Informational observation, optional fix | --- ## Verdict Logic Static checks alone determine FAIL (blockers found). The runtime check from Step 7 determines whether a blocker-free sync is in the PASS family. The wire-evidence gate from Step 7b then determines whether a runtime-clean sync can be promoted to SYNC COMPLETE for UCs with `actor != SYSTEM`. Apply rules in order — first match wins: ``` if (blocker_count > 0) or (mock_blockers > 0): verdict = "FAIL" → headline: SYNC INCOMPLETE — REGRESSION (return to dev) elif runtime_result == "NO_INFRA": verdict = "NO_INFRA" → headline: SYNC APPLIED — NO_INFRA elif runtime_result == "RUNNER_BROKEN": verdict = "RUNNER_BROKEN" → headline: SYNC APPLIED — RUNNER_BROKEN elif runtime_result == "REGRESSION": verdict = "REGRESSION" → headline: SYNC INCOMPLETE — REGRESSION elif wire_evidence_required and not wire_evidence_present: # Step 7b strict-only gate. actor != SYSTEM AND no recognised # wire-evidence:* artifact present and runnable. verdict = "UNVERIFIED" → headline: SYNC APPLIED — UNVERIFIED (wire-evidence missing) elif (mock_warnings > 0) or (fe_coverage_gap == true) or (runtime_result == "UNVERIFIED"): verdict = "UNVERIFIED" → headline: SYNC APPLIED — UNVERIFIED elif runtime_result == "BLOCKED": verdict = "BLOCKED" → headline: SYNC APPLIED — BLOCKED elif runtime_result == "PASS" and warning_count > 0: verdict = "PASS_WITH_WARNINGS" → headline: SYNC COMPLETE (with warnings) else: verdict = "PASS" → headline: SYNC COMPLETE ``` `wire_evidence_required` is `true` when the UC's graph node carries `actor != SYSTEM` (any human or external-provider actor). It is `false` for purely internal UCs. The flag is property-driven, not operator- driven; there is no `--skip-wire-evidence` flag and no inline override. The only override is a signed exception under the schema defined by W4. **Mock-specific verdict notes:** - `mock_blockers > 0`: a production file (non-test, non-fixture, non-stories) imports from a mock module. This is an incompatibility that causes runtime drift — treated identically to a structural BLOCKER. - `mock_warnings > 0`: WARNING-severity mock remnants were found (e.g., hardcoded data in non-service FE code). The sync cannot be declared complete — downgrade to UNVERIFIED. - `fe_coverage_gap = true`: FE test files use `jest.mock(`, `vi.mock(`, `setupServer(`, or `mockapi.` — they intercept requests rather than hitting a real BE. Endpoint coverage via these tests is not genuine; downgrade to UNVERIFIED. | Headline | Next step | |----------|-----------| | `SYNC COMPLETE` | Proceed to `/nacl-tl-qa UC###` or `/nacl-tl-docs UC###` | | `SYNC COMPLETE` (with warnings) | Proceed with warnings included in QA/review checklist | | `SYNC INCOMPLETE — REGRESSION` | Return to `/nacl-tl-dev-be UC### --continue` or `/nacl-tl-dev-fe UC### --continue` | | `SYNC APPLIED — UNVERIFIED` | Endpoint paths not covered by any test — add coverage or accept gap | | `SYNC APPLIED — UNVERIFIED (wire-evidence missing)` | Add a `wire-evidence:fixture:`, `wire-evidence:contract-test:`, or `wire-evidence:live-smoke:` artifact, OR file a signed exception under the W4 schema. No inline override. | | `SYNC APPLIED — NO_INFRA` | One or both workspaces missing test runner — add infra | | `SYNC APPLIED — RUNNER_BROKEN` | Runner crashed — diagnose environment before proceeding | | `SYNC APPLIED — BLOCKED` | Pre-existing unrelated failures — user decides whether to proceed | --- ## Output: sync-report.md Create `.tl/tasks/UC###/sync-report.md` using `nacl-tl-core/templates/sync-report-template.md`. The report MUST include: 1. **YAML frontmatter** -- task_id, verdict, stats, timestamps, commits 2. **Summary** -- verdict, contract version, check counts 3. **Contract Compliance** -- each endpoint with BE status, FE status 4. **Type Consistency** -- each shared type with BE match, FE match 5. **Error Handling** -- each error code per endpoint, BE returns, FE handles 6. **Mock Remnants** -- scan results or "no mock remnants found" 7. **Auth Flow** -- each protected endpoint, BE middleware, FE auth header 8. **Issues** -- detailed BLOCKER/WARNING/INFO with file, line, description, fix 9. **Runtime Checks** -- BE suite result, FE suite result, endpoint coverage table 10. **Recommendations** -- ordered by severity, actionable fix instructions 11. **Verdict Justification** -- why this verdict, what is the next step --- ## Status Update ### On SYNC COMPLETE ```json { "phases": { "sync": "done" }, "sync_completed": "YYYY-MM-DDTHH:MM:SSZ", "sync_verdict": "PASS" } ``` ### On FAIL / REGRESSION / UNVERIFIED / NO_INFRA / RUNNER_BROKEN ```json { "phases": { "sync": "failed" }, "sync_completed": "YYYY-MM-DDTHH:MM:SSZ", "sync_verdict": "FAIL", "sync_blockers": ["B1: description", "B2: description"] } ``` ### Changelog Entry Append to `.tl/changelog.md`: ```markdown ## [YYYY-MM-DD HH:MM] SYNC: UC### - Title - Phase: Sync Verification - Headline: SYNC COMPLETE / SYNC APPLIED — UNVERIFIED / SYNC INCOMPLETE — REGRESSION / ... - Static checks: N passed, M warnings, K blockers - Runtime: BE suite {PASS/NO_INFRA/RUNNER_BROKEN/REGRESSION}, FE suite {PASS/...} - Endpoint coverage: N/M endpoints covered by tests - Endpoints verified: N ``` --- ## Recovery: On FAIL When the verdict is FAIL or SYNC INCOMPLETE — REGRESSION, the report must clearly specify: 1. **Which side needs fixing** (BE, FE, or both) 2. **Exact file and line** where the problem is 3. **What the contract says** (expected) 4. **What the code does** (actual) 5. **How to fix it** (specific instructions) The developer runs `/nacl-tl-dev-be UC### --continue` or `/nacl-tl-dev-fe UC### --continue`, then re-runs `/nacl-tl-sync UC###`. **Maximum iterations**: 3. After the third FAIL, mark the task as `blocked` in status.json and escalate for manual intervention. --- ## Output Summary ### On SYNC COMPLETE ``` Sync Verification Complete Task: UC### [Title] Headline: SYNC COMPLETE Static: N/N endpoints in sync | Types: N/N consistent | Errors: N/N covered | Mocks: 0 Runtime: BE suite PASS (N tests) | FE suite PASS (N tests) Endpoint coverage: N/M endpoints covered by tests Report: .tl/tasks/UC###/sync-report.md Next: /nacl-tl-qa UC### or /nacl-tl-docs UC### ``` ### On SYNC COMPLETE (with warnings) ``` Sync Verification Complete Task: UC### [Title] Headline: SYNC COMPLETE (M warnings) Runtime: BE suite PASS | FE suite PASS Report: .tl/tasks/UC###/sync-report.md Next: /nacl-tl-qa UC### (warnings included in checklist) ``` ### On SYNC APPLIED — UNVERIFIED ``` Sync Verification: SYNC APPLIED — UNVERIFIED Task: UC### [Title] Static checks: PASS (no blockers) Runtime: BE suite PASS | FE suite PASS Endpoint coverage gaps: - POST /api/orders — no test references this path in BE or FE Report: .tl/tasks/UC###/sync-report.md Options: (a) Add endpoint tests: /nacl-tl-dev-be UC### --continue (b) Accept gap and proceed: /nacl-tl-qa UC### ``` ### On SYNC APPLIED — NO_INFRA / RUNNER_BROKEN ``` Sync Verification: SYNC APPLIED — {NO_INFRA|RUNNER_BROKEN} Task: UC### [Title] Static checks: PASS (no blockers) Runtime: {workspace} — {NO_INFRA: scripts.test missing | RUNNER_BROKEN: runner crashed} Report: .tl/tasks/UC###/sync-report.md Next: Add test runner for {workspace} workspace, then re-run /nacl-tl-sync UC### ``` ### On SYNC INCOMPLETE — REGRESSION ``` Sync Verification: SYNC INCOMPLETE — REGRESSION Task: UC### [Title] Blockers: K found B1: [description] -- fix in [BE/FE] -- [file]:[line] Report: .tl/tasks/UC###/sync-report.md Fix: /nacl-tl-dev-be UC### --continue | /nacl-tl-dev-fe UC### --continue Then: /nacl-tl-sync UC### ``` --- ## Error Handling ### Task Not Found ``` Error: Task UC### not found Expected: .tl/tasks/UC###/ Run: /nacl-tl-plan to create the task first. ``` ### API Contract Missing ``` Error: API contract not found for UC### Expected: .tl/tasks/UC###/api-contract.md Run: /nacl-tl-plan UC### to generate the API contract. ``` ### BE or FE Not Approved ``` Error: {{Backend/Frontend}} not approved for UC### Current phases.review_{{be/fe}}: {{status}} Expected: "approved" or "done" Run: /nacl-tl-review UC### --{{be/fe}} ``` ### Result Files Missing ``` Error: Implementation results not found for UC### Missing: {{result-be.md / result-fe.md}} Run: /nacl-tl-dev-be UC### and/or /nacl-tl-dev-fe UC### first. ``` ### Source Code Not Found If a referenced source file does not exist on disk, mark as BLOCKER in the report with description "missing implementation file". --- ## Reference Documents | Purpose | File | |---------|------| | Sync verification rules | `nacl-tl-core/references/sync-rules.md` | | API contract format | `nacl-tl-core/references/api-contract-rules.md` | | Stub tracking rules | `nacl-tl-core/references/stub-tracking-rules.md` | ## Templates - `nacl-tl-core/templates/sync-report-template.md` -- Sync report output format ## Examples - `nacl-tl-core/examples/sync-report-example.md` -- Complete sync report example --- ## Procedural Checklist ### Before Starting - [ ] Task directory exists with api-contract.md - [ ] Both phases.review_be and phases.review_fe are "approved" or "done" - [ ] result-be.md and result-fe.md exist ### During Static Verification - [ ] API contract fully parsed (endpoints, types, errors, auth) - [ ] BE source scanned for all contract endpoints - [ ] FE source scanned for all contract endpoint calls - [ ] Endpoint compliance checked (method, path, params) - [ ] Request DTOs compared (field names, types, required/optional) - [ ] Response DTOs compared (field names, types, nested objects) - [ ] Error handling verified for each endpoint - [ ] Auth flow verified for all protected endpoints - [ ] Shared types checked for consistency and no duplication - [ ] Mock remnants scanned in production FE code ### During Runtime Verification (Step 7) - [ ] BE workspace `scripts.test` discovered (or NO_INFRA recorded) - [ ] FE workspace `scripts.test` discovered (or NO_INFRA recorded) - [ ] BE suite run; pass/fail counts captured - [ ] FE suite run; pass/fail counts captured - [ ] Endpoint path coverage grep run for each touched endpoint (BE and FE) using `grep -F` (literal match) - [ ] FE test files scanned for `jest.mock(` / `vi.mock(` / `setupServer(` / `mockapi.` patterns; `fe_coverage_gap` recorded - [ ] Production paths scanned for mock imports (`import .* from .*mock`); `mock_blockers` and `mock_warnings` counts recorded - [ ] Runtime result classified (PASS / UNVERIFIED / NO_INFRA / RUNNER_BROKEN / REGRESSION / BLOCKED) ### During Wire-Evidence Gate (Step 7b) - [ ] UC `actor` resolved from graph (`SYSTEM` vs. external/human) - [ ] If `actor != SYSTEM`: at least one `wire-evidence:fixture:`, `wire-evidence:contract-test:`, or `wire-evidence:live-smoke:` artifact identified - [ ] Artifact existence on disk confirmed - [ ] If the artifact is a runnable test, it was run and exited 0 - [ ] Recorded evidence string(s) appended to `Task.verification_evidence` - [ ] No inline override applied (override path is a signed exception under W4 only) ### After Verification - [ ] sync-report.md created with all sections filled (including runtime check section) - [ ] Headline assigned based on combined static + runtime result - [ ] status.json updated (phases.sync) - [ ] changelog.md updated - [ ] Next steps clearly stated --- ## Next Steps **SYNC COMPLETE / SYNC COMPLETE (with warnings):** `/nacl-tl-qa UC###` (E2E testing) or `/nacl-tl-docs UC###` (documentation) **SYNC INCOMPLETE — REGRESSION:** `/nacl-tl-dev-be UC### --continue` or `/nacl-tl-dev-fe UC### --continue`, then re-run `/nacl-tl-sync UC###` **SYNC APPLIED — UNVERIFIED:** Add endpoint-level tests or accept gap; re-run `/nacl-tl-sync UC###` **SYNC APPLIED — NO_INFRA / RUNNER_BROKEN:** Fix test infrastructure, then re-run `/nacl-tl-sync UC###`