{
  "blueprint": "timesheets",
  "builder": "Claude, following the phase prompts in order (a fresh subagent per phase that saw only the spec and the build folder)",
  "testedSpecSha256": "140726440f4607c4e0e78774aeb534fae37d9d2ce0ce6e146bcf3554b4fa82f0",
  "reviewDate": "2026-10-30",
  "reviewMethod": "Playwright scripts (Chromium) clicked through the build as the named seed people and asserted on what was on screen, with direct requests where a check says the server must refuse something; the approved-hours export was downloaded and parsed. The run below is the single final run on a fresh database after all spec gaps were fixed, resetting to the seed before each phase's checks. Earlier per-phase runs found the gaps and are not counted. Results are final post-fix checks, not first-attempt success rates or an independent human audit.",
  "automatedTests": {
    "passed": 123,
    "total": 123,
    "command": "npm test",
    "rerunDuringReview": true
  },
  "gapsFixed": 21,
  "runs": [
    {
      "startedAt": "2026-10-30T08:57:37.208Z",
      "finishedAt": "2026-10-30T08:57:55.298Z",
      "phases": [
        1,
        2,
        3,
        4
      ],
      "passed": 21,
      "total": 21,
      "items": [
        {
          "phase": 1,
          "text": "Signed in as Dana, Admin: Clients and projects shows Website redesign at $150.00 with a 120 h budget owned by Maya Chen, and its three assigned people.",
          "status": "pass",
          "evidence": "Row: \"Website redesign Billable · $150.00/h 120 h Maya Chen Jo Park, Lee Park, Maya Chen Edit Website redesign\". Owner Maya Chen; assigned Jo Park, Lee Park, Maya Chen (3 people)."
        },
        {
          "phase": 1,
          "text": "Signed in as Jo, Admin pages say \"Only admins can open this page.\" and a direct request to change a rate is refused.",
          "status": "pass",
          "evidence": "Jo's nav: \"My week Approvals Projects Reports\" (no admin links). Pages: /admin/people 403, /admin/clients 403, /admin/settings 403, /admin/projects/project_website 403, each \"Only admins can open this page.\". Direct POST rate=$1.00 -> 403 \"Only admins can open this page.\"; Dana still sees $150.00."
        },
        {
          "phase": 1,
          "text": "Saving a billable project without a rate is refused.",
          "status": "pass",
          "evidence": "New billable project \"Support retainer\" with no rate: \"A billable project needs a rate.\"; not created. Blanking Brand refresh's rate: \"A billable project needs a rate.\"; still $140.00."
        },
        {
          "phase": 1,
          "text": "Deactivating Maya Chen is refused because she is the approver of Jo's submitted week and owns an active project.",
          "status": "pass",
          "evidence": "Refused: \"Can't deactivate Maya Chen until these are reassigned: approver of Jo Park's submitted timesheet for the week of Oct 19; owner of the active project Acme Corp · Website redesign.\". People row: \"Maya Chen maya@fernway.example Dana Ruiz No 40 h Active Edit Maya Chen\"."
        },
        {
          "phase": 2,
          "text": "Signed in as Lee, My week for the current week shows 6.00 h on Website redesign on Monday and the status Draft.",
          "status": "pass",
          "evidence": "\"My week Oct 26 to Nov 1, 2026\"; Website redesign row [\"6.00 h\",\"\",\"\",\"\",\"\",\"\",\"\",\"6.00 h\"]; status \"Draft Not submitted yet. You can submit this week from Friday, Oct 30.\"; week total 6.00 h."
        },
        {
          "phase": 2,
          "text": "Adding 1:45 on Website redesign works; adding 1:40 is refused; adding an entry that takes one day over 24 hours is refused.",
          "status": "pass",
          "evidence": "1:45 added on 2026-10-27 (Tuesday cell 1.75 h). 1:40 refused: \"1:40 isn't a 15-minute step. Use 0:15 steps, like 1:30 or 1:45.\". 18:15 on Monday (already 6:00) refused: \"A day can't have more than 24 hours. Mon Oct 26 already has 6.00 h, so you can add at most 18:00 more.\". Week total 7.75 h."
        },
        {
          "phase": 2,
          "text": "Adding time to Brand refresh is not possible for Lee (he is not assigned), and a direct request to do so is refused.",
          "status": "pass",
          "evidence": "Picker offers: Acme Corp · Website redesign | Internal · Admin and meetings. Direct POST /entries project_brand -> 403 \"You aren't assigned to Bluebird · Brand refresh, so you can't log time to it.\"; no Brand refresh row."
        },
        {
          "phase": 2,
          "text": "Submitting the current week before Friday is refused; after moving the clock to Friday, Lee submits it, the banner says \"Submitted to Maya Chen\", and the outbox has \"Timesheet to approve\" for Maya.",
          "status": "pass",
          "evidence": "Clock 2026-10-27: \"You can submit this week from Friday, Oct 30.\". Clock set to 2026-10-30 09:00; Lee submitted; banner \"Submitted to Maya Chen on Fri Oct 30, 2026 09:00. Entries are locked while it waits for approval. Withdraw it to make changes.\". Outbox: to maya@fernway.example, \"Timesheet to approve: Lee Park, week of Oct 26 (7.75 h)\" (logged)."
        },
        {
          "phase": 2,
          "text": "While submitted, Lee cannot edit or delete an entry, including through a direct request; withdrawing returns the week to Draft and emails Maya.",
          "status": "pass",
          "evidence": "Submitted week shows no edit, delete or add forms. Direct edit -> 409 \"This timesheet is submitted, so its entries are locked. Withdraw it to make changes.\"; delete -> 409; add -> 409; Monday still 6.00 h. Withdraw -> \"Draft Not submitted yet. Submit it when your hours are complete.\", entry editable again. Outbox: to maya@fernway.example, \"Withdrawn: Lee Park's timesheet for the week of Oct 26\"."
        },
        {
          "phase": 2,
          "text": "Signed in as Ben, submitting last week with no entries is refused until he adds the note \"On leave\", then it submits to Sam Okafor.",
          "status": "pass",
          "evidence": "Week of Oct 19, 0.00 h. Submit without note: \"This week has no hours. Add your time, or write a note of 5 to 200 characters (for example \"On leave\") to submit it without hours.\". With \"On leave\": banner \"Submitted to Sam Okafor on Fri Oct 30, 2026 09:00. Entries are locked while it waits for approval. Withdraw it to make changes.\"; outbox to sam@fernway.example: \"Timesheet to approve: Ben Cho, week of Oct 19 (0.00 h)\"."
        },
        {
          "phase": 3,
          "text": "Signed in as Maya, Approvals lists Jo Park's week of Oct 5 with 38.00 h; approving it locks it and emails Jo.",
          "status": "pass",
          "evidence": "Approvals row: \"Jo Park Week of Oct 19 38.00 h 8.50 h Fri Oct 23, 2026 17:15 Open the full week Approve Comment for Jo Park (5 to 300 ch\". After Approve, Jo's week banner \"Approved by Maya Chen on Oct 27 Approved weeks are locked. A\"; no add/edit forms; direct add -> 409 \"This timesheet is approved, so its entries are locked. An admin can withdraw the approval to unlock it.\". Outbox to jo@fernway.example: \"Approved: your timesheet for the week of Oct 19\"."
        },
        {
          "phase": 3,
          "text": "Signed in as Dana, Projects shows Website redesign at 80% (96 of 120 h) \"plus 6.00 h in drafts\", and Brand refresh at 37%.",
          "status": "pass",
          "evidence": "Signed in as Dana (admin). Website redesign: \"80% (96 of 120 h)\", \"plus 6.00 h in drafts\", bar with 80% mark. Brand refresh: \"37% (22.5 of 60 h)\", \"plus 3.00 h in drafts\"."
        },
        {
          "phase": 3,
          "text": "After Dana changes the Website redesign rate to $160, Lee's approved weeks still value at $150 per hour, while his draft hours show $160.",
          "status": "pass",
          "evidence": "Rate saved as $160.00. Project page, Lee's weeks: Week of Oct 26 Draft 6.00 h at $160.00/h = $960.00; Week of Oct 19 Approved 36.00 h at $150.00/h = $5,400.00; Week of Oct 12 Approved 38.00 h at $150.00/h = $5,700.00. Entries on Lee's approved week of Oct 19: $150.00/h."
        },
        {
          "phase": 3,
          "text": "Ben submits last week with the note \"On leave\"; signed in as Sam, rejecting it without a comment is refused, and with a comment it returns to Draft and Ben is emailed.",
          "status": "pass",
          "evidence": "Ben submitted with \"On leave\" (\"Submitted to Sam Okafor on Tue Oct 27, 2\"). Sam rejected without comment: \"A comment saying what needs to change is required (5 to 300 characters).\". With comment: Ben's week \"Draft Not submitted yet. Submit it when your hours are complete.\". Outbox to ben@fernway.example: \"Changes needed on your timesheet for the week of Oct 19\", body includes the comment."
        },
        {
          "phase": 3,
          "text": "Dana withdraws approval of Lee's week of Oct 5 with a reason: it becomes Draft and editable, and both Lee and Maya are emailed.",
          "status": "pass",
          "evidence": "Lee's week of Oct 19 was \"Approved by Maya Chen on Oct 27 Approved\". Without reason: \"A reason for withdrawing the approval is required (5 to 300 characters).\". With reason: \"Draft Not submitted yet.\". Outbox \"Approval withdrawn: week of Oct 19\" to lee@fernway.example and maya@fernway.example. Lee edited Thursday's Website redesign entry to 6:00 (grid 6.00 h)."
        },
        {
          "phase": 3,
          "text": "Submitting enough hours to take Website redesign past 120 h sends Maya the 100% warning once, and no second 80% warning.",
          "status": "pass",
          "evidence": "Lee resubmitted last week and submitted this week (30.00 h); burn \"104% (125 of 120 h)\". Outbox: 1 x \"Website redesign has used 100% of its budget (125 of 120 h)\" to Maya; 80% emails: 1 (the seeded one). Jo then submitted 2 h more (burn \"105% (127 of 120 h)\"): still 1 x 100% and 1 x 80%."
        },
        {
          "phase": 3,
          "text": "Maya submits a week of her own: it goes to Dana Ruiz, and Maya has no Approve button on it.",
          "status": "pass",
          "evidence": "Maya's banner \"Submitted to Dana Ruiz on Fri Oct 30, 20\". Approve buttons for Maya: My week 0, week page 0, her Approvals list 0. Dana's Approvals lists it. Direct POST /weeks/week_117ca9fe35a8448690ec/approve as Maya -> 403 \"You can't approve or reject your own timesheet.\"."
        },
        {
          "phase": 4,
          "text": "Moving the clock to Friday 15:00 and running the job reminds everyone whose current week is a draft, once; running it again sends nothing new.",
          "status": "pass",
          "evidence": "14:00 run: no reminders. 15:00 run: 6 x \"Please submit your timesheet for the week of Oct 26\" to ben@fernway.example, dana@fernway.example, jo@fernway.example, lee@fernway.example, maya@fernway.example, sam@fernway.example. Lee's email: \"So far you have entered 6.00 h, which is below your weekly capacity of 40.\". 16:00 run: still 6. POST /cron/hourly without secret -> 401."
        },
        {
          "phase": 4,
          "text": "Moving the clock to the next Monday 10:00 and running the job reminds people whose previous week is still a draft.",
          "status": "pass",
          "evidence": "Lee submitted the week of Oct 26 on Friday. Clock 2026-11-02 09:00: no new reminders (6 from Friday). 10:00 run, new: \"Please submit your timesheet for the week of Oct 26\" to ben@fernway.example, dana@fernway.example, jo@fernway.example, maya@fernway.example, sam@fernway.example (not Lee). Cron endpoint with secret -> 200, still 5."
        },
        {
          "phase": 4,
          "text": "Reports grouped by client for the last 30 days shows Acme Corp's billable value from approved and submitted hours, with approved and submitted shown separately.",
          "status": "pass",
          "evidence": "\"By client, Oct 4 to Nov 2, 2026\". Acme Corp: billable value $15,300.00; approved 94.00 h $14,100.00; submitted 8.00 h $1,200.00. Not counted: 3.00 h in drafts in this range."
        },
        {
          "phase": 4,
          "text": "The approved-hours export for Acme Corp contains only entries from approved weeks, with rate and value columns, and no entries from Jo's submitted week.",
          "status": "pass",
          "evidence": "Downloaded approved-hours_acme-corp_2026-10-04_to_2026-11-02.csv: header \"Date,Client,Project,Person,Hours,Note,Rate,Value\"; 13 rows, all Acme Corp, people Jo Park, Lee Park; 94 h, $14100.00 (approved weeks only); 0 rows from Jo's submitted week of Oct 19 (which has 2.00 h on Website redesign). Example: \"2026-10-12,Acme Corp,Website redesign,Jo Park,8.00,Content inventory,150.00,1200.00\"."
        }
      ]
    }
  ],
  "notTested": [
    "Production sign-in: a trusted-header path for the platform's sign-in was built, but every check used the development \"sign in as\" switcher.",
    "Real email delivery: notifications and reminders were written to the outbox and logged, not sent; a failing provider was simulated only in automated tests.",
    "The hourly job on a real scheduler: it was run from \"Run the hourly job now\" with the development clock, and /cron/hourly was called directly once with and once without the secret.",
    "Two approvers or admins acting on the same timesheet at once from two browsers: the version check is covered by automated tests only.",
    "Spec fixes that no acceptance check covers (the email_domain setting, the three-attempt retry limit and the new event types) were written after the build; the test build handles these through its own choices.",
    "Hosting: the build ran on SQLite on one server."
  ]
}