All features

Feature breakdown · built with CraftHQ

Users & accessAdmin

Add a user from the company directory

Hands-on build time

~1.8 to 2.8 h

Measured — hands-on build time, from the visible commits

Whole-team time at this build's pace

Estimated ~5 h

2 people incl. the product owner, requirements to merge

Estimate at this build's pace, not a measurement of this feature

An admin adds a user by their directory id; the id is verified against the company directory and the name is filled in, and the user is added once the admin confirms. Nothing is emailed.

Size: 6 FP · In-house IFPUG-style count, cross-checked by a second counter; not certified. Range 4 to 10. · How we counted 6 FP

How long a feature this size usually takes

Whole-team time — this build vs a typical team

6 FP · FP × 6.8 to 9.4 h (ISBSG 2023)

Whole-team time at this build's pace2 people incl. the product owner, requirements to merge
~5 h

Estimate at this build's pace, not a measurement of this feature

A typical team, whole lifecycleRequirements to release · middle half of 512 comparable projects (Java, Agile), ISBSG 2023
~41 to 56 habout 1–1.5 person-weeksmedian ~47 h

Hands-on build time: ~1.8 to 2.8 h, measuredhands-on build time, from the visible commits

grey band = a typical team, middle halfhatched = our whole-team estimate

Both figures are whole-team time over the whole lifecycle, so they compare like for like. Ours is this build's pace — about 0.8 hours per function point across the whole build (two people, including the product owner, with requirements and review time). The industry range is an ISBSG project-level figure applied to one feature, so it is indicative.

Workflow

How it moved through the workflow

Select a stage. The build shipped, so every stage is complete; a person approved the merge.

Request

Add a user by their directory ID, verified live against the directory, with the name filled in, so identities are not mistyped.

Questions

One decision went to the business before building: show the directory lookup to the admin, or do it silently.

Approved

The product owner signs off the breakdown by moving the story into the approved state, and the build runs against that reference.

Definition of ready

The questions are answered.
The acceptance criteria are testable.
The size is estimated.
The scope is checked for splitting.
The product owner has signed off.

Building

CraftHQ's build agents write the code and the tests together, run as a loop; a failing check halts the work until it passes.

Checks

Three checks run on every pull request: one on the product owner's machine as CraftHQ builds, one on the engineer's machine before review, and a code-quality scan in CI. Merging is gated by the CI check passing and a person's approval, enforced by branch protection.

Review

An engineer reviews the pull request and merges it.

Merged

The pull request is merged to main after a person approves it. Nothing merges without that approval.

Definition of done

Every acceptance criterion is checked; where a scenario has a dedicated test it is cited, and where it was verified by review that is stated.
The three checks are green.
A person approved the merge.

Breakdown

The feature breakdown

Business first, then the engineering view. Tab through it.

Overview

The gap it closes

Typing a new user's name and identity by hand invites mistakes and a mismatch with the real directory.

Who uses it

Administrators adding a user to their organisation.

User story

As an admin, I want to add a user by their directory ID so that their identity is directory-verified and their name is filled in for me.

Examples

Example 1

An admin enters a valid directory ID. The directory is checked, the name is filled in, and the admin adds the user.

Example 2

An admin enters a directory ID that does not resolve. The admin sees a clear rejection, and no user is added.

Dependencies & risks

Depends on

A configuration-driven staff-domain allowlist, and a reachable company directory to verify the id and return the name.

Risk & mitigation

A wrong-domain or an identity that cannot be verified could be added. Reduced by a domain allowlist checked before any directory call and again on create (the same primitives as the sign-in path), and by confirming the id against the directory and failing closed on an unknown or unreachable result.

How it works

Simplified diagrams of the parts involved, the steps of one request, and where it runs.

Architecture

People
Adminadds a user
Your app
Invite formdirectory-ID field
Directory lookupverifies and auto-fills
User storeverified identities
External services
Company directoryverifies the ID
  • 1enter a directory ID
  • 2check the ID
  • 3verify, read the name
  • 4add the verified user
Simplified.

Sequence

Browserthe admin
App serverthe directory lookup
User storewrite
Company directoryverifies the ID
  • 1add a user by directory ID
  • 2verify the ID
  • 3resolved name or not found
  • 4show the check result
  • 5admin confirms
  • 6add the verified user
Simplified.

Infrastructure

Client (untrusted)
Web appthe invite form
Your app
App serverdomain allowlist and directory lookup; authenticates, pins the tenant
Databaseper-tenant isolation, user rows
External services
Company directoryverifies IDs
  • 1add by directory ID
  • 2verify the ID
  • 3write the verified user
Simplified.

Acceptance criteria

Each scenario is written as a Gherkin spec. The Engineering tab shows the test that covers each one, or notes where it was verified by review.

@users-access @admin
Feature: Add a user from the company directory
As an admin
I want to add a user by their directory id
So that their identity is directory-verified and their name is filled in for me
Adding a user takes a directory id, verifies it against the company directory, fills in the name and, once the admin confirms, adds the user. Nothing is emailed; the user is added directly.
Background:
Given an admin is signed in
Rule: Only an admin may add a user from the directory
Scenario: A non-admin cannot add a user
Given a signed-in user without the admin role
When they call the add-from-directory action
Then the action is refused on the server
Rule: A new user is verified against the directory before the admin confirms
Scenario: Adding a user by a valid directory id
Given an admin enters a valid directory id for an active directory account on an approved domain
When the directory is checked
Then the directory result and the filled-in name are shown to the admin
And the user is added only after the admin confirms
And the user is stored with the directory's name and immutable object id
Scenario: A directory id that does not resolve is rejected
Given an admin enters a directory id that does not resolve
When the directory is checked
Then the admin sees a clear rejection
And no user is added
Rule: Unsafe or duplicate directory accounts are refused
Scenario Outline: Directory ids that are refused
Given an admin enters a directory id that is "<condition>"
When the directory is checked
Then the add is refused with a clear message
And no user is added
Examples:
| condition |
| already a user in this organisation |
| a disabled account in the directory |
| on a domain not in the approved list |
Rule: A directory outage fails closed
Scenario: The directory is unreachable
Given the company directory is unreachable
When an admin tries to add a user by directory id
Then the add cannot proceed and the admin is told to try again later
And no user is added

Questions

Questions raised in planning, each with the decision taken.

Should the directory lookup be shown to the admin, or done silently?

Showing the check tells the admin the identity is verified; a silent lookup hides whether it succeeded.

AShow the check to the admin, with the name shown from the directory. · recommended
BDo the lookup silently in the background.
Decided: Show the check; names are shown from the directory (option A).

How we sized it

A function point measures what the software does — its inputs, outputs, lookups and the data it stores — regardless of language or how many lines it took. Each counted item is weighted by complexity: a user input counts 3–6 points, an output or report 4–7, a lookup 3–6, data the feature owns 7–10, and data read from elsewhere 5–7.

  • Add a user by their directory ID, verified against the company directory
    User input
    6
  • Total
    6
6 function points (range 4–10). IFPUG-style count: each user action, lookup, output and stored data set, weighted by complexity. Technical plumbing is excluded, and a data store is counted once in the feature that owns it.

Not counted here

  • The user records: counted once, under User management

Components touched

Domain allowlist

The email domain must be on a configuration-driven staff-domain allowlist, checked in the service before any directory call and again on create, using the same primitives as the sign-in path. A wrong-domain email is hard-rejected.

Live directory lookup

Confirms the entered directory id against the company directory before a user is added and shows the result to the admin; an unknown or unreachable result is refused (fail-closed). The preview endpoint is admin-only and uses the same resolution, so it cannot drift from the add.

Name auto-fill

Fills the name from the directory, shown back to the admin as a verified result; the user is stored and matched on the immutable directory object id, not on an email that can change.

Rejection messages

A clear admin-facing message when an id does not resolve, is already a user, is a disabled account, or is on a non-approved domain.

Data touched

User rowswrite

A new user is tied to the verified directory identity by its immutable object id; email uniqueness is case-insensitive and enforced in the database, and a cross-tenant mapping is blocked.

Company directoryexternal

It is read to verify the id and return the name. The immutable directory object id is recorded on first add and matched on afterwards.

Security decisions

  • Only an admin role reaches the add-from-directory action and the preview, enforced on the server.
  • The email domain must be on a configured allowlist, checked before any directory call and again on create.
  • The identity is verified against the company directory and matched on the immutable object id, failing closed on an unknown or unreachable result.
  • A directory id that is already a user, a disabled account, or on a non-approved domain is refused, and no user is created.
  • Email uniqueness is case-insensitive and enforced in the database, and a cross-tenant mapping is blocked.

Subtasks

  • A configuration-driven staff-domain allowlist checked before any directory call and on create.
  • A live directory lookup on add, matched on the immutable object id.
  • Name auto-fill from the directory, and an admin-only preview using the same resolution.
  • Clear admin rejection messages.

How each scenario is verified

  • A wrong-domain email is refused before any directory work
    A domain-enforcement test rejects a disallowed public domain with the contracted message before any database work and passes a configured domain, reusing the sign-in allowlist primitives.
  • Adding by a directory id and the sign-in path
    Covered by the directory and sign-in path tests, including alias de-duplication.
  • An unresolved directory id is refused
    Verified by review of the fail-closed path.

Risk tier

Logic A person approves every merge. This is a Logic-tier change; it carries automated tests for its rules.

Activity

The visible commits (not squashed) cluster into about 1.8 to 2.8 hours of focused work.

  • First of three visible commits
  • Corrected and completed
  • Merged to main after a person approved

Start here

Want this on your backlog?

Get a free baseline report on one repository, or scope a 3-month pilot measured against what your team’s work costs today.

Measured from your own git history
Every pull request tested before review
A person approves every merge