ai-engineering-loop 1.0.13 → 1.0.15

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.
Files changed (47) hide show
  1. package/.agents/devil-advocate.md +1 -1
  2. package/.agents/judge.md +1 -1
  3. package/.agents/workflows/ai-engineering-loop.md +1 -1
  4. package/.claude/agents/devil-advocate.md +1 -1
  5. package/.claude/agents/judge.md +1 -1
  6. package/.claude/skills/ai-engineering-loop/SKILL.md +2 -2
  7. package/.gemini/skills/ai-engineering-loop/SKILL.md +2 -2
  8. package/.grok/agents/devil-advocate.md +1 -1
  9. package/.grok/agents/judge.md +1 -1
  10. package/.grok/skills/ai-engineering-loop/SKILL.md +2 -2
  11. package/README.md +4 -4
  12. package/adapters/dot/README.md +8 -8
  13. package/adapters/dot/coreview.md +3 -2
  14. package/adapters/dot/gitlab.md +3 -3
  15. package/adapters/dot/mattermost.md +59 -71
  16. package/adapters/dot/multi-branch.md +3 -2
  17. package/adapters/dot/skills/dot-dev-workflow/SKILL.md +2 -2
  18. package/adapters/dot/skills/task-impact-inquiry/SKILL.md +155 -0
  19. package/agents/judge.md +2 -1
  20. package/agents/maker.md +1 -1
  21. package/agents/shared/devil-advocate.body.md +1 -1
  22. package/agents/shared/judge.body.md +1 -1
  23. package/bin/ai-engineering-loop.js +2 -2
  24. package/core/goal-contract.md +10 -3
  25. package/core/grill-policy.md +17 -11
  26. package/core/judge-policy.md +1 -0
  27. package/core/verification-loop.md +3 -1
  28. package/examples/backend-api/payment-idempotency/README.md +3 -3
  29. package/examples/dot/status-display/README.md +23 -0
  30. package/examples/dot/status-display/delivery-report.md +41 -0
  31. package/examples/dot/status-display/goal-contract.md +46 -0
  32. package/examples/dot/status-display/judge-verdict.md +44 -0
  33. package/examples/dot/status-display/review-findings.md +63 -0
  34. package/examples/initialization/README.md +2 -2
  35. package/examples/initialization/discovery-trace.md +1 -1
  36. package/examples/mobile-app/offline-sync-queue/README.md +3 -3
  37. package/lib/sync-hosts.js +4 -1
  38. package/package.json +1 -1
  39. package/policies/tdd-policy.md +23 -4
  40. package/tests/no-company-leak.test.js +82 -0
  41. package/tests/skill-host-compat.test.js +54 -7
  42. package/tests/sync-hosts.test.js +19 -1
  43. package/examples/dot/attendance-confirmation/README.md +0 -22
  44. package/examples/dot/attendance-confirmation/delivery-report.md +0 -51
  45. package/examples/dot/attendance-confirmation/goal-contract.md +0 -39
  46. package/examples/dot/attendance-confirmation/judge-verdict.md +0 -43
  47. package/examples/dot/attendance-confirmation/review-findings.md +0 -57
@@ -14,20 +14,39 @@ No test is written at an unconfirmed seam. If the contract omitted seams and gri
14
14
 
15
15
  The test reads like a specification of behavior at the seam. Names use `.ai-engineering-loop/glossary.md`. The test survives an internal rewrite. Expected values come from the Goal Contract or a known-good literal, not from re-running the implementation.
16
16
 
17
+ ## Failure table
18
+
19
+ The Goal Contract's AC-1..N **are** the test list. Each row is one red test at the named seam. Do not invent a parallel "comprehensive suite" of only positive cases.
20
+
21
+ Default rows unless the contract marked them N/A with one sentence:
22
+
23
+ 1. Happy path
24
+ 2. Empty / omitted / null
25
+ 3. Boundary (min, max, off-by-one, locked vs open)
26
+ 4. Sibling / isolation (other entities in the same parent must not change)
27
+ 5. Error / denied / unauthorized / invalid input
28
+
29
+ Do not stop after the first green test. A suite that never asserts a failure mode does not cover the feature.
30
+
17
31
  ## Loop
18
32
 
19
- 1. **Red.** Write one failing test for one AC slice. Confirm it fails for the right reason.
33
+ 1. **Red.** Write one failing test for one AC row. Confirm it fails for the right reason.
20
34
  2. **Green.** Write the smallest production change that passes that test.
21
- 3. Repeat one slice at a time (vertical). Do not write the whole suite first.
22
- 4. After slices covering AC-1..N, run the full verification commands in `.ai-engineering-loop/verification.md` and keep the Evidence Contract.
35
+ 3. Repeat one row at a time (vertical). Do not write the whole suite first.
36
+ 4. After AC-1..N are green, run the full verification commands in `.ai-engineering-loop/verification.md` and keep the Evidence Contract.
37
+ 5. **Coverage as a map, not a score.** If a coverage command exists, use the report to find branches that map to a written AC and are still untested. Add tests only for those. Do not chase 90% with tautological asserts. Line coverage without a failure table is invalid evidence.
38
+ 6. **Mutation (optional).** If `.ai-engineering-loop/verification.md` names a mutation tool (Stryker, mutmut, PIT, cargo-mutants), run it on the files this task touched. Surviving mutants on an AC path are ITERATE. Do not add mutation as a new stage or a global 90% gate.
39
+ 7. **Property-based (optional).** When the input space is broad (ids, dates, strings, amounts), one property per invariant beats a pile of copied positives. Not required for every task.
23
40
 
24
41
  ## Forbidden tests
25
42
 
26
43
  - **Implementation-coupled:** mocks internal collaborators, tests private methods, or asserts through a side channel (raw DB) instead of the seam.
27
44
  - **Tautological:** expected value is computed the same way as the code (`expect(add(a,b)).toBe(a+b)`).
28
45
  - **Source grep:** `grep -q 'featureFlag' src/file` is not a test of the AC. Prove behavior at the named seam or on the named artifact.
46
+ - **Happy-path only:** every test is a success case while the contract listed empty, boundary, sibling, or error rows.
47
+ - **Coverage theater:** raising percent with `toBeDefined()`, snapshot-without-oracle, or tests that cannot fail.
29
48
  - **Horizontal slicing:** all tests first, then all implementation.
30
49
 
31
50
  ## Evidence
32
51
 
33
- Stage 5 still requires command, exit code 0, stdout, test counts, and assertionEvidence mapped to AC ids. "We did TDD" is not evidence. A red-then-green story without logs is invalid.
52
+ Stage 5 still requires command, exit code 0, stdout, test counts, and assertionEvidence mapped to AC ids. "We did TDD" is not evidence. A red-then-green story without logs is invalid. assertionEvidence must cite at least one non-happy-path AC when the failure table has one.
@@ -0,0 +1,82 @@
1
+ 'use strict';
2
+
3
+ const test = require('node:test');
4
+ const assert = require('node:assert');
5
+ const fs = require('fs');
6
+ const path = require('path');
7
+
8
+ const ROOT = path.join(__dirname, '..');
9
+
10
+ const SKIP_DIR = new Set([
11
+ '.git',
12
+ 'node_modules',
13
+ '.serena',
14
+ '.DS_Store'
15
+ ]);
16
+
17
+ function walk(dir, out = []) {
18
+ for (const name of fs.readdirSync(dir)) {
19
+ if (SKIP_DIR.has(name)) continue;
20
+ const abs = path.join(dir, name);
21
+ const st = fs.lstatSync(abs);
22
+ if (st.isSymbolicLink()) continue;
23
+ if (st.isDirectory()) walk(abs, out);
24
+ else if (st.isFile()) out.push(abs);
25
+ }
26
+ return out;
27
+ }
28
+
29
+ function rel(abs) {
30
+ return path.relative(ROOT, abs).split(path.sep).join('/');
31
+ }
32
+
33
+ const FINGERPRINTS = [
34
+ { re: /dotify/i, label: 'dotify' },
35
+ { re: /bikin-rindu/i, label: 'bikin-rindu' },
36
+ { re: /hasNormalHours/, label: 'hasNormalHours' },
37
+ { re: /overtimeNote/, label: 'overtimeNote' },
38
+ { re: /attendanceConfirmation/i, label: 'attendanceConfirmation' },
39
+ { re: /internal-dotify/, label: 'internal-dotify' },
40
+ { re: /hanaaaca/, label: 'coworker handle' },
41
+ { re: /ulfa\.mufida/, label: 'coworker handle' },
42
+ { re: /kontribusi\/mattermost-agent/, label: 'local mattermost-agent checkout' },
43
+ { re: /dot-system\//, label: 'dot-system/' },
44
+ { re: /timeEntities/, label: 'timeEntities' },
45
+ { re: /resolveAttendanceConfirmation/, label: 'resolveAttendanceConfirmation' },
46
+ { re: /attendance-confirmations/, label: 'attendance-confirmations' }
47
+ ];
48
+
49
+ test('packaged files do not embed real client tickets, schema, or machine paths', () => {
50
+ const files = walk(ROOT).filter((abs) => {
51
+ const r = rel(abs);
52
+ return !r.startsWith('tests/no-company-leak.test.js');
53
+ });
54
+ const hits = [];
55
+ for (const abs of files) {
56
+ const r = rel(abs);
57
+ let text;
58
+ try {
59
+ text = fs.readFileSync(abs, 'utf8');
60
+ } catch {
61
+ continue;
62
+ }
63
+ if (text.includes('\u0000')) continue;
64
+
65
+ if (r !== 'bin/ai-engineering-loop.js' && /gitlab\.dot\.co\.id/.test(text)) {
66
+ hits.push(`${r}: gitlab.dot.co.id (allowed only as adapter auto-detect in bin/)`);
67
+ }
68
+ if (r === 'bin/ai-engineering-loop.js' && /gitlab\.dot\.co\.id\/.+/.test(text)) {
69
+ hits.push(`${r}: gitlab.dot.co.id with a path (ticket URL)`);
70
+ }
71
+
72
+ const inSensitiveTree = r.startsWith('adapters/') || r.startsWith('examples/');
73
+ if (inSensitiveTree && /\/Users\/egagofur/.test(text)) {
74
+ hits.push(`${r}: /Users/egagofur`);
75
+ }
76
+
77
+ for (const { re, label } of FINGERPRINTS) {
78
+ if (re.test(text)) hits.push(`${r}: ${label}`);
79
+ }
80
+ }
81
+ assert.deepStrictEqual(hits, []);
82
+ });
@@ -166,17 +166,33 @@ test('Gemini Antigravity skill matches the loop and never uses Grok spawn keys o
166
166
  assert.match(gemini, /TRUE_INDEPENDENT_AGENT/);
167
167
  });
168
168
 
169
- test('DOT adapter locates task-impact-inquiry on Antigravity Gemini skills, not Claude', () => {
169
+ test('task-impact-inquiry is Claude-safe and ships to Claude, Grok, and Gemini', () => {
170
+ const skill = readRepo('adapters/dot/skills/task-impact-inquiry/SKILL.md');
171
+ const { fm } = parseFrontmatter(skill, 'task-impact-inquiry');
172
+ assert.doesNotMatch(fm, /^description:\s*>-?/m);
173
+ assert.doesNotMatch(skill, /```mermaid/);
174
+ assert.doesNotMatch(skill, /\$\\/);
175
+ assert.doesNotMatch(skill, /spawn_subagent/);
176
+ assert.match(skill, /lifecycle/);
177
+ assert.match(skill, /ASCII/);
178
+ assert.match(skill, /failure table/);
179
+ assert.match(skill, /Do not interview again/);
180
+ assert.match(skill, /Passing unit tests are not isolation proof|Green unit tests/);
181
+ assert.match(skill, /New-dev briefing/);
182
+ assert.match(skill, /Hit map|Where it hits/);
183
+ assert.match(skill, /Do not generate an FSD/);
184
+ const grill = readRepo('core/grill-policy.md');
185
+ assert.match(grill, /Business blast radius/);
186
+ assert.match(grill, /~\/\.claude\/skills\/task-impact-inquiry/);
187
+ assert.match(grill, /~\/\.grok\/skills\/task-impact-inquiry/);
188
+ assert.match(grill, /~\/\.gemini\/config\/skills\/task-impact-inquiry/);
170
189
  const readme = readRepo('adapters/dot/README.md');
171
- assert.match(readme, /~\/\.gemini\/config\/skills/);
172
190
  assert.match(readme, /task-impact-inquiry/);
173
- assert.match(readme, /not from `~\/\.claude\/skills\/`/);
174
- assert.doesNotMatch(readme, /and `~\/\.claude\/skills\/`/);
175
- const grill = readRepo('core/grill-policy.md');
176
- assert.match(grill, /task-impact-inquiry/);
177
- assert.match(grill, /Do not skip grill/);
191
+ assert.match(readme, /~\/\.claude\/skills\//);
192
+ assert.doesNotMatch(readme, /not from `~\/\.claude\/skills\/`/);
178
193
  const wf = readRepo('.agents/workflows/ai-engineering-loop.md');
179
194
  assert.match(wf, /task-impact-inquiry/);
195
+ assert.match(wf, /blast radius: lifecycle sketch/);
180
196
  });
181
197
 
182
198
  test('DOT router sends commit-bound work to ai-engineering-loop; workflow is Stage 8 only', () => {
@@ -233,6 +249,37 @@ test('Grill freeze gate and idea menu are in the loop, not a parallel product',
233
249
  }
234
250
  });
235
251
 
252
+ test('Failure table is required to freeze, TDD, verify, and Judge', () => {
253
+ const grill = readRepo('core/grill-policy.md');
254
+ assert.match(grill, /failure table/);
255
+ assert.match(grill, /sunny path is not frozen/);
256
+ const contract = readRepo('core/goal-contract.md');
257
+ assert.match(contract, /The Happy-Path Contract/);
258
+ assert.match(contract, /Empty \/ omitted field/);
259
+ const tdd = readRepo('policies/tdd-policy.md');
260
+ assert.match(tdd, /Coverage as a map, not a score/);
261
+ assert.match(tdd, /Happy-path only/);
262
+ assert.match(tdd, /Coverage theater/);
263
+ const verif = readRepo('core/verification-loop.md');
264
+ assert.match(verif, /non-happy-path AC/);
265
+ const judge = readRepo('agents/shared/judge.body.md');
266
+ assert.match(judge, /happy-path-only/);
267
+ const da = readRepo('agents/shared/devil-advocate.body.md');
268
+ assert.match(da, /happy-path-only suite vs written failure table/);
269
+ for (const rel of [
270
+ '.claude/skills/ai-engineering-loop/SKILL.md',
271
+ '.grok/skills/ai-engineering-loop/SKILL.md',
272
+ '.gemini/skills/ai-engineering-loop/SKILL.md',
273
+ '.agents/workflows/ai-engineering-loop.md'
274
+ ]) {
275
+ const text = readRepo(rel);
276
+ assert.match(text, /failure table/, rel);
277
+ assert.match(text, /One red test per AC row/, rel);
278
+ assert.match(text, /Do not freeze sunny-path-only/, rel);
279
+ assert.match(text, /blast radius: lifecycle sketch/, rel);
280
+ }
281
+ });
282
+
236
283
  test('Host skills absorb grill, TDD, glossary, and two-axis review without splitting the loop', () => {
237
284
  for (const rel of [
238
285
  '.claude/skills/ai-engineering-loop/SKILL.md',
@@ -47,9 +47,27 @@ test('sync upserts Claude AEL files but does not invent DOT skills', () => {
47
47
  assert.ok(fs.existsSync(path.join(home, '.claude/agents/judge.md')));
48
48
  assert.ok(fs.existsSync(path.join(home, '.claude/commands/ai-engineering-loop.md')));
49
49
  assert.ok(!fs.existsSync(path.join(home, '.claude/skills/dot-dev-workflow/SKILL.md')));
50
+ assert.ok(fs.existsSync(path.join(home, '.claude/skills/task-impact-inquiry/SKILL.md')));
50
51
  assert.ok(!fs.existsSync(path.join(home, '.claude/settings.local.json')));
51
52
  });
52
53
 
54
+ test('sync upserts task-impact-inquiry onto Claude, Grok, and Gemini', () => {
55
+ const home = tmpHome();
56
+ fs.mkdirSync(path.join(home, '.claude'));
57
+ fs.mkdirSync(path.join(home, '.grok'));
58
+ fs.mkdirSync(path.join(home, '.gemini'));
59
+ applyHostSync({ packageRoot: ROOT, home });
60
+ const claude = fs.readFileSync(path.join(home, '.claude/skills/task-impact-inquiry/SKILL.md'), 'utf8');
61
+ const grok = fs.readFileSync(path.join(home, '.grok/skills/task-impact-inquiry/SKILL.md'), 'utf8');
62
+ const gemini = fs.readFileSync(path.join(home, '.gemini/config/skills/task-impact-inquiry/SKILL.md'), 'utf8');
63
+ assert.match(claude, /name: task-impact-inquiry/);
64
+ assert.doesNotMatch(claude, /```mermaid/);
65
+ assert.match(claude, /New-dev briefing/);
66
+ assert.match(grok, /^user-invocable: true$/m);
67
+ assert.match(grok, /Where it hits/);
68
+ assert.match(gemini, /Business blast radius|blast radius/);
69
+ });
70
+
53
71
  test('DOT skills update only when already installed', () => {
54
72
  const home = tmpHome();
55
73
  const dest = path.join(home, '.claude/skills/dot-dev-workflow/SKILL.md');
@@ -84,7 +102,7 @@ test('dry-run does not write; second apply is current', () => {
84
102
  assert.ok(!fs.existsSync(path.join(home, '.gemini/config/skills/ai-engineering-loop/SKILL.md')));
85
103
  applyHostSync({ packageRoot: ROOT, home });
86
104
  const again = applyHostSync({ packageRoot: ROOT, home });
87
- assert.strictEqual(summarizeHostSync(again.filter((item) => item.id === 'gemini')).current, 1);
105
+ assert.strictEqual(summarizeHostSync(again.filter((item) => item.id === 'gemini')).current, 2);
88
106
  assert.strictEqual(summarizeHostSync(again.filter((item) => item.id === 'gemini')).copy, 0);
89
107
  });
90
108
 
@@ -1,22 +0,0 @@
1
- # Reference Example: Attendance Confirmation Status Fix
2
-
3
- ## 1. Context & Scenario
4
-
5
- This reference walkthrough demonstrates how a complex real-world bug in the DOT ecosystem moves through the complete **AI Engineering Loop** and is subsequently delivered via the **DOT Delivery Adapter**.
6
-
7
- ### The Problem
8
- In the Dotify employee portal, attendance confirmation cards displayed an incorrect status (`"PENDING"`) for employees working overtime or on weekends, even when all underlying time logs had already been approved or rejected. Furthermore, non-normal hours employees (shift workers) had their records mistakenly treated under standard 9-to-5 rules.
9
-
10
- ---
11
-
12
- ## 2. Walkthrough Stages & Artifacts
13
-
14
- 1. **[Stage 1: Goal Contract (`goal-contract.md`)](file:///Users/egagofur/Development/work/ai-engineering-loop/examples/dot/attendance-confirmation/goal-contract.md)**:
15
- - Formalized objective, acceptance criteria (normal hours, weekends, non-normal hours, null values), and verification plan.
16
- 2. **[Stage 2: Adversarial Review & Triage (`review-findings.md`)](file:///Users/egagofur/Development/work/ai-engineering-loop/examples/dot/attendance-confirmation/review-findings.md)**:
17
- - Independent Devil's Advocate review flagging a timezone offset bug (`COR-001`) and an invalid nitpick (`MAINT-001`).
18
- - Maker Agent triage and surgical resolution.
19
- 3. **[Stage 3: Judge Evaluation & Verdict (`judge-verdict.md`)](file:///Users/egagofur/Development/work/ai-engineering-loop/examples/dot/attendance-confirmation/judge-verdict.md)**:
20
- - Audit of unit test results, typecheck logs, finding resolutions, and issuance of `PASS` verdict.
21
- 4. **[Stage 4: DOT Delivery Pipeline (`delivery-report.md`)](file:///Users/egagofur/Development/work/ai-engineering-loop/examples/dot/attendance-confirmation/delivery-report.md)**:
22
- - Creation of GitLab Issue #307, base MR !946, multi-branch propagation to `staging` (!947) and `develop` (!948), Coreview bot triage, and automated Mattermost channel notification.
@@ -1,51 +0,0 @@
1
- # DOT Delivery Report: Attendance Confirmation Fix
2
-
3
- ## 1. Summary of Delivery Actions
4
-
5
- Upon receiving the `PASS` verdict from the Judge Agent, the DOT Delivery Adapter executed the following release actions:
6
-
7
- 1. **GitLab Issue Created**: [Issue #307 - [BE] [Attendance] Fix Attendance Confirmation Display Status](https://gitlab.dot.co.id/dot-system/dotify-new/-/issues/307).
8
- 2. **Primary MR Created**: [MR !946 targeting `main`](https://gitlab.dot.co.id/dot-system/dotify-new/-/merge_requests/946).
9
- 3. **Multi-Branch Cherry-Pick**:
10
- - [MR !947 targeting `staging`](https://gitlab.dot.co.id/dot-system/dotify-new/-/merge_requests/947).
11
- - [MR !948 targeting `develop`](https://gitlab.dot.co.id/dot-system/dotify-new/-/merge_requests/948).
12
- 4. **Coreview Bot Triage**:
13
- - Comments fetched on MR !948. Zero blocking comments found.
14
- 5. **Mattermost Notification**:
15
- - Resolved repository `dot-system/dotify-new` to channel `"internal-dotify"`.
16
- - Dispatched Markdown report via MCP `mattermost_send_message` with `from: "AI Agent"`.
17
-
18
- ---
19
-
20
- ## 2. GitLab Links
21
-
22
- | Artifact | Branch | Link |
23
- |---|---|---|
24
- | **GitLab Issue** | — | `[Issue #307](https://gitlab.dot.co.id/dot-system/dotify-new/-/issues/307)` |
25
- | **Merge Request DEV** | `develop` | `[MR !948](https://gitlab.dot.co.id/dot-system/dotify-new/-/merge_requests/948)` |
26
- | **Merge Request STAGING** | `staging` | `[MR !947](https://gitlab.dot.co.id/dot-system/dotify-new/-/merge_requests/947)` |
27
- | **Merge Request MAIN** | `main` | `[MR !946](https://gitlab.dot.co.id/dot-system/dotify-new/-/merge_requests/946)` |
28
-
29
- ---
30
-
31
- ## 3. Dispatched Mattermost Notification
32
-
33
- ```text
34
- [MR DEV] https://gitlab.dot.co.id/dot-system/dotify-new/-/merge_requests/948
35
- Changes log
36
- - Include user.type.hasNormalHours and timeEntities.overtimeNote in attendanceConfirmationPagination Prisma query.
37
- - Create resolveAttendanceConfirmationDisplayStatus utility to properly evaluate hasPendingTimeEntities by checking overtimeNote, isWeekend, duration > 8, non-normal hours employees, and null statuses.
38
- - Add comprehensive unit tests in src/server/attendance-confirmations/utils/resolve-display-status.test.ts covering all status permutations.
39
-
40
- [MR STAGING] https://gitlab.dot.co.id/dot-system/dotify-new/-/merge_requests/947
41
- Changes log
42
- - Include user.type.hasNormalHours and timeEntities.overtimeNote in attendanceConfirmationPagination Prisma query.
43
- - Create resolveAttendanceConfirmationDisplayStatus utility to properly evaluate hasPendingTimeEntities by checking overtimeNote, isWeekend, duration > 8, non-normal hours employees, and null statuses.
44
- - Add comprehensive unit tests in src/server/attendance-confirmations/utils/resolve-display-status.test.ts covering all status permutations.
45
-
46
- [MR MAIN] https://gitlab.dot.co.id/dot-system/dotify-new/-/merge_requests/946
47
- Changes log
48
- - Include user.type.hasNormalHours and timeEntities.overtimeNote in attendanceConfirmationPagination Prisma query.
49
- - Create resolveAttendanceConfirmationDisplayStatus utility to properly evaluate hasPendingTimeEntities by checking overtimeNote, isWeekend, duration > 8, non-normal hours employees, and null statuses.
50
- - Add comprehensive unit tests in src/server/attendance-confirmations/utils/resolve-display-status.test.ts covering all status permutations.
51
- ```
@@ -1,39 +0,0 @@
1
- # Goal Contract: Attendance Confirmation Display Status Fix
2
-
3
- ## 1. Objective
4
- Fix the incorrect calculation of attendance confirmation display statuses across list and pagination endpoints when time entities contain overtime notes, weekend logs, non-normal hours employees, or null record states.
5
-
6
- ## 2. Business Outcome & User Lifecycle Impact
7
- - **Employees**: View accurate confirmation statuses (APPROVED, REJECTED, NEED_APPROVAL) reflecting their true attendance record.
8
- - **Managers / Reviewers**: Stop receiving false-positive pending approval notifications for already-settled weekend logs.
9
- - **HR & Payroll**: Accurate cumulative duration calculations for payroll export.
10
-
11
- ## 3. Acceptance Criteria (AC)
12
- - [ ] **AC-1**: If an employee has `hasNormalHours = true` and `duration <= 8` on a standard weekday without overtime notes, display status must resolve to `APPROVED` or `NEED_APPROVAL` strictly based on clock-in/out presence.
13
- - [ ] **AC-2**: If `overtimeNote` exists or `duration > 8` or `isWeekend = true`, evaluate pending status across all associated `timeEntities`.
14
- - [ ] **AC-3**: Non-normal hours employees (`user.type.hasNormalHours = false`) must not be evaluated under 8-hour weekday thresholds.
15
- - [ ] **AC-4**: Null or missing time entity collections must default safely to `APPROVED` without throwing runtime `TypeError`.
16
- - [ ] **AC-5**: Backward compatibility of the tRPC/REST response contract must be strictly preserved.
17
-
18
- ## 4. Technical Constraints
19
- - Preserve existing Prisma query schemas in `attendanceConfirmationPagination`.
20
- - Centralize logic in a pure, testable utility function: `resolveAttendanceConfirmationDisplayStatus`.
21
- - No new external runtime dependencies (use existing `dayjs` and `lodash` packages).
22
- - 0 TypeScript compiler errors on `tsc --noEmit`.
23
-
24
- ## 5. Out of Scope
25
- - Modifying the UI frontend component layouts in `dotify-new/web`.
26
- - Database schema migrations or alter table statements.
27
- - Changing payroll export report generation scripts.
28
-
29
- ## 6. Verification Requirements
30
- - **Unit Tests**: Comprehensive Jest test suite in `src/server/attendance-confirmations/utils/resolve-display-status.test.ts` covering 100% of branches.
31
- - **Typecheck**: `npx tsc --noEmit` exits with 0.
32
- - **Linter**: `npx eslint --fix` on modified files with 0 errors.
33
- - **Regression**: Run entire test suite: `npx jest --testPathIgnorePatterns="dotify-api"`.
34
-
35
- ## 7. Definition of Done (DoD)
36
- - [ ] All AC-1 through AC-5 proven by unit tests.
37
- - [ ] Deterministic verification passes 100%.
38
- - [ ] Devil's Advocate review conducted with 0 unresolved blocking findings.
39
- - [ ] Judge issues PASS verdict.
@@ -1,43 +0,0 @@
1
- # Judge Evaluation Report: Attendance Confirmation Fix
2
-
3
- ## 1. Executive Verdict
4
- - **Verdict**: `PASS`
5
- - **Iteration**: `Iteration 2 of 3`
6
- - **Confidence**: `HIGH`
7
-
8
- ---
9
-
10
- ## 2. Deterministic Verification Audit
11
-
12
- | Check | Command Executed | Raw Result | Status |
13
- |---|---|---|:---:|
14
- | **Unit Tests** | `npx jest src/server/attendance-confirmations/utils/resolve-display-status.test.ts` | `Tests: 12 passed, 12 total. Snapshots: 0. Time: 1.42s` | ✅ PASS |
15
- | **Full Suite** | `npx jest --testPathIgnorePatterns="dotify-api"` | `Test Suites: 48 passed, 48 total. Tests: 382 passed.` | ✅ PASS |
16
- | **TypeScript** | `npx tsc --noEmit` | `Exit code 0. Zero errors.` | ✅ PASS |
17
- | **Linter** | `npx eslint --fix src/server/attendance-confirmations/**` | `0 errors, 0 warnings found.` | ✅ PASS |
18
-
19
- ---
20
-
21
- ## 3. Goal Contract Compliance Audit
22
-
23
- | Criterion | Verified By Test / Artifact | Result |
24
- |---|---|:---:|
25
- | **AC-1**: Normal hours weekday calculation | `resolve-display-status.test.ts > normal hours` | ✅ PASS |
26
- | **AC-2**: Weekend & duration > 8 handling | `resolve-display-status.test.ts > weekend overtime` | ✅ PASS |
27
- | **AC-3**: Non-normal hours employee rules | `resolve-display-status.test.ts > shift worker` | ✅ PASS |
28
- | **AC-4**: Null & undefined timeEntities safety | `resolve-display-status.test.ts > null safety` | ✅ PASS |
29
- | **AC-5**: API Contract Preservation | `tRPC router typecheck` | ✅ PASS |
30
-
31
- ---
32
-
33
- ## 4. Finding Triage Audit
34
-
35
- - **COR-001 (High - Null Safety)**: Maker applied null coalescing in Iteration 2. Verified via fresh test execution. **Status: RESOLVED & VERIFIED**.
36
- - **MAINT-001 (Low - Factory Suggestion)**: Overridden by Judge as invalid speculative nitpick. **Status: DISMISSED / INVALID**.
37
-
38
- ---
39
-
40
- ## 5. Formal Conclusion & Hand-off
41
-
42
- The Definition of Done has been 100% satisfied with reproducible evidence.
43
- **Authorized next action**: Proceed to [DOT Delivery Adapter](file:///Users/egagofur/Development/work/ai-engineering-loop/adapters/dot/README.md) for GitLab MR generation, multi-branch cherry-picking, Coreview triage, and Mattermost notification.
@@ -1,57 +0,0 @@
1
- # Adversarial Review Findings & Triage: Attendance Confirmation Fix
2
-
3
- ## 1. Review Summary
4
-
5
- - **Reviewer**: Devil's Advocate Agent
6
- - **Target Branch**: `main...fix/attendance-confirmation-status`
7
- - **Total Findings**: 2
8
- - **Blocking (SEV-1/2)**: 1
9
- - **Non-Blocking / Invalid**: 1
10
-
11
- ---
12
-
13
- ## 2. Findings Ledger
14
-
15
- ### Finding COR-001: Missing null safety when iterating `timeEntities`
16
- - **Severity**: `HIGH` (SEV-2)
17
- - **Category**: `Correctness`
18
- - **Location**: `src/server/attendance-confirmations/utils/resolve-display-status.ts:34-41`
19
- - **Evidence**:
20
- ```typescript
21
- const hasPending = confirmation.timeEntities.some(
22
- (entity) => entity.status === "NEED_APPROVAL"
23
- );
24
- ```
25
- - **Problem**:
26
- If `confirmation.timeEntities` is `null` or `undefined` (which occurs for historical attendance records prior to migration v2.4), calling `.some()` throws a runtime `TypeError: Cannot read properties of undefined (reading 'some')`.
27
- - **Impact**:
28
- Crashing pagination API for employees with legacy historical attendance records.
29
- - **Recommendation**:
30
- ```diff
31
- - const hasPending = confirmation.timeEntities.some(
32
- + const hasPending = (confirmation.timeEntities ?? []).some(
33
- (entity) => entity.status === "NEED_APPROVAL"
34
- );
35
- ```
36
- - **Confidence**: `HIGH`
37
- - **Status**: `TRIAGED_VALID`
38
- - **Resolution**:
39
- Maker Agent applied optional chaining and null coalescing `(confirmation.timeEntities ?? [])` and added unit test case `should return APPROVED when timeEntities is null or undefined` in `resolve-display-status.test.ts`.
40
-
41
- ---
42
-
43
- ### Finding MAINT-001: Suggestion to convert helper into an abstract factory class
44
- - **Severity**: `LOW` (SEV-4)
45
- - **Category**: `Maintainability`
46
- - **Location**: `src/server/attendance-confirmations/utils/resolve-display-status.ts:1-50`
47
- - **Evidence**:
48
- Utility is authored as a pure exported function `export function resolveAttendanceConfirmationDisplayStatus(...)`.
49
- - **Problem**:
50
- Reviewer claimed that an object-oriented Strategy/Factory pattern would allow swapping attendance status calculators in the future.
51
- - **Impact**:
52
- None. Pure functions are more testable, tree-shakeable, and align with current repository conventions.
53
- - **Recommendation**: Refactor to `class AttendanceStatusCalculatorFactory`.
54
- - **Confidence**: `LOW`
55
- - **Status**: `TRIAGED_INVALID`
56
- - **Triage Reason**:
57
- Dismissed as speculative overengineering. Repository architecture uses functional utilities with tRPC. Adding a class factory violates Technical Constraint: *"Produce minimal, surgical changes without speculative abstractions."*