Core Part4 of 5
Version0.1.0-draft
Last Updated2025-11-30
StatusDraft
TypeNORMATIVE (Core)

WDP Part 4: Sequence Field Specification

Specification of the Sequence Field (Part 4) of the Waddling Diagnostic Protocol (WDP) error code structure. The sequence field uniquely identifies a specific diagnostic code within the Component.Primary namespace.

Abstract#

This document specifies the Sequence Field (Part 4) of the Waddling Diagnostic Protocol (WDP) error code structure. The sequence field uniquely identifies a specific diagnostic code within the Component.Primary namespace, providing the most granular level of classification.

Specification Navigation: See STRUCTURE.md for an overview of all WDP specification parts.

Conformance

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.

1. Introduction#

The sequence field is the fourth part of the five-part WDP error code structure:

Complete WDP Structure:

Severity.Component.Primary.Sequence -> CompactID
    ^         ^        ^        ^          ^
  Part 1    Part 2   Part 3   Part 4    Part 5

This Document Focus:

Severity.Component.Primary.Sequence -> CompactID
                            ^^^^^^^^
                            Part 4: Unique identifier within category (THIS DOCUMENT)

The sequence field provides the final level of specificity, uniquely identifying a particular diagnostic event within the Component.Primary namespace.

2. Purpose#

The sequence field serves to:

  1. Uniquely identify specific diagnostic codes within a category
  2. Enable evolution - Add new error codes over time without breaking existing ones
  3. Provide ordering for related diagnostics
  4. Support conventions for common error patterns (see 7-SEQUENCE-CONVENTIONS.md)
  5. Form canonical identity for hash computation

Example

Component.Primary = Auth.Token

Sequences within Auth.Token:
  001 - Token missing
  002 - Token malformed
  003 - Token invalid signature
  004 - Token expired
  005 - Token not yet valid
  ...

Catalog Evolution Example:

Version 1.0:  E.Auth.Token.001, E.Auth.Token.003, E.Auth.Token.018
Version 1.1:  Add E.Auth.Token.031 (new error type)
Version 1.2:  Add E.Auth.Token.032 (another new error type)

→ Existing codes (001-003) remain unchanged
→ New codes added incrementally
→ No breaking changes to existing systems

3. Format Options#

WDP supports two sequence format options:

3.1 Option 1: Numeric (RECOMMENDED)

Pattern: Exactly three digits (zero-padded)
Regex: [0-9]{3}
Range: 001 to 999

Examples:

001
002
003
...
999

3.2 Option 2: Named (OPTIONAL)

Pattern: SCREAMING_SNAKE_CASE
Regex: [A-Z][A-Z0-9_]*

Examples:

MISSING
INVALID
EXPIRED
NOT_FOUND
TOKEN_REVOKED

Important: Named sequences are display aliases only. They MUST be resolved to their numeric canonical form before hash computation.

4. Field Specification#

4.1 Field Properties

PropertyValue
PositionFourth field (rightmost before compact ID)
RequiredYES
Canonical FormNumeric (001-999)
Display FormNumeric OR Named (alias)
Hash InputAlways numeric canonical form

4.2 Character Sets

Numeric sequences:

  • ASCII digits only: 0-9
  • Exactly 3 characters
  • Zero-padded (e.g., 001, not 1)

Named sequences:

  • ASCII uppercase letters: A-Z
  • ASCII digits: 0-9
  • Underscore: _
  • Must start with uppercase letter
  • No maximum length (but keep reasonable, 8-24 chars recommended)

5. Numeric Sequences (Canonical)#

5.1 Overview

Numeric sequences are the canonical identity for WDP error codes.

Range: 001 to 999 (999 possible sequences per Component.Primary)
Format: Exactly three digits, zero-padded

Examples:

001, 002, 003, ..., 098, 099, 100, ..., 999

Important: The zero-padding is REQUIRED and is part of the canonical identity used for hash computation.

5.2 Benefits

Numeric sequences provide:

  • Simple allocation - Easy to increment and assign
  • Clear ordering - Natural sequence for related errors
  • Stable identity - Can change associated names without affecting hash
  • Compact display - Short and readable
  • Convention support - Works well with semantic conventions (001=MISSING, etc.)
  • Internationalization - Language-independent canonical form

5.3 Range Sufficiency

999 sequences per Component.Primary is sufficient for most use cases:

Component SizeTypical Error Count
Small5-20 errors
Medium20-50 errors
Large50-100 errors
Very Large100-500 errors (rare)

If you need more than 999 errors in a single Component.Primary:

This is often a design smell. Consider splitting into more specific subcategories:

Instead of:
E.Parser.*.001-999  (too many in one Primary)

Consider:
E.Parser.Syntax.001-150
E.Parser.Semantic.001-100
E.Parser.Lexical.001-50
E.Parser.Type.001-80

5.4 Format Rules

MUST:

  • Use exactly three digits
  • Zero-pad values less than 100 (e.g., 001, not 1)
  • Stay within range 001-999

MUST NOT:

  • Use 000 (reserved/invalid)
  • Exceed 999
  • Omit leading zeros

Hash Input: When computing the compact ID hash, the numeric sequence MUST be zero-padded:

✅ Correct:   E.AUTH.TOKEN.001  (hash input)
❌ Incorrect: E.AUTH.TOKEN.1    (missing padding)

6. Named Sequences (Display Aliases)#

6.1 Overview

Named sequences are OPTIONAL display aliases for numeric sequences. They provide semantic meaning in code and documentation but are NOT used for hash computation.

Key Principle: Named sequences MUST resolve to numeric sequences before hashing.

6.2 Format Rules

Pattern: SCREAMING_SNAKE_CASE
Regex: [A-Z][A-Z0-9_]*

Rules:

  • Must start with uppercase letter (A-Z)
  • May contain uppercase letters, digits, and underscores
  • No lowercase letters, hyphens, or other special characters
  • Recommended length: 8-24 characters

Examples:

MISSING          ✅
INVALID          ✅
EXPIRED          ✅
NOT_FOUND        ✅
TOKEN_REVOKED    ✅
ALREADY_EXISTS   ✅
PERMISSION_DENIED ✅

Invalid:

missing          ❌ Must be uppercase
Missing          ❌ Must be SCREAMING_SNAKE_CASE
not-found        ❌ No hyphens allowed
NOT FOUND        ❌ No spaces allowed
_MISSING         ❌ Must start with letter

6.3 Relationship to Numeric

Canonical Identity: Numeric sequences (001-999)
Associated Name: Semantic alias for documentation
Display: Can show numeric, name, or both
Hash Input: Always numeric

Example:

Canonical:       E.Auth.Token.001
Associated Name: TOKEN_MISSING
Display Forms:   E.Auth.Token.001
                 E.Auth.Token.TOKEN_MISSING
                 E.Auth.Token.001 (TOKEN_MISSING)
Hash Input:      E.AUTH.TOKEN.001  (always numeric)
Hash Output:     Xy8Qz

6.4 Benefits

Named sequences provide:

  • Self-documenting - Semantic meaning visible in code
  • Memorable - Easier to remember than numbers
  • Readable - Clear intent in logs and error messages
  • Code constants - Natural fit for programming language constants

6.5 Rationale for Resolution

Why resolve named → numeric before hashing?

  1. Stable Identity: Renaming aliases doesn't change the hash
  2. i18n Ready: Names can be localized while numeric identity remains universal
  3. Simplicity: Hash input contains only alphanumeric + period (no underscores)
  4. Flexibility: Names are optional and implementation-defined
  5. Deterministic: Same numeric sequence always produces same hash

7. Hash Canonical Form#

7.1 Hash Input Normalization

Before computing the compact ID hash, the sequence field MUST be:

  1. Resolved to numeric (if named alias is used)
  2. Part of uppercase-normalized full code

7.2 Normalization Flow

Numeric sequence (no resolution needed):

Display:     E.Auth.Token.001
             ↓ normalize to uppercase
Hash Input:  E.AUTH.TOKEN.001  (note: 001 with zero-padding)
             ↓ hash (xxHash64 → Base62)
Compact ID:  Xy8Qz

Key Point: The numeric sequence in the hash input MUST be zero-padded to 3 digits:

  • Sequence 1 → Hash input uses 001
  • Sequence 42 → Hash input uses 042
  • Sequence 123 → Hash input uses 123

Named sequence (resolve first):

Display:     E.Auth.Token.TOKEN_MISSING
             ↓ resolve named → numeric
Resolved:    E.Auth.Token.001
             ↓ normalize to uppercase
Hash Input:  E.AUTH.TOKEN.001
             ↓ hash (xxHash64 → Base62)
Compact ID:  Xy8Qz  (same as numeric!)

7.3 Key Properties

Same numeric sequence → Same hash:

E.Auth.Token.001              → Xy8Qz
E.Auth.Token.TOKEN_MISSING    → Xy8Qz  (resolves to 001)
E.Auth.Token.MISSING          → Xy8Qz  (resolves to 001)

All produce the same hash because they all resolve to:
E.AUTH.TOKEN.001  (with zero-padded 001)

Zero-padding matters for hash computation:

✅ E.AUTH.TOKEN.001 → Xy8Qz  (correct format)
❌ E.AUTH.TOKEN.1   → ???    (invalid - different hash, don't use!)

Different numeric sequences → Different hashes:

E.Auth.Token.001  → Xy8Qz
E.Auth.Token.003  → Ab3Cd
E.Auth.Token.018  → Mn7Pq

8. Choosing Between Formats#

8.1 Decision Matrix

ConsiderationNumericNamed
Canonical identity✅ Yes❌ No (must resolve)
Hash input✅ Direct⚠️ Requires resolution
Simple allocation✅ Easy increment⚠️ Requires naming
Self-documenting⚠️ Needs docs✅ Semantic meaning
Code readability⚠️ Numbers cryptic✅ Clear intent
Internationalization✅ Universal⚠️ Language-specific
Stable hash✅ Rename-safe✅ (if resolved)

8.2 Recommendation: Hybrid Approach

Best Practice: Use numeric sequences as canonical identity and associate semantic names for documentation and code constants.

Pattern:

Rust
// Define named constants that map to numeric values
const TOKEN_MISSING: u32 = 001;
const TOKEN_EXPIRED: u32 = 002;
const TOKEN_INVALID: u32 = 003;

// Use in code
fn create_error() -> String {
    format!("E.Auth.Token.{:03}", TOKEN_MISSING)
    // Result: "E.Auth.Token.001"
}

Catalog documents both:

Json
{
  "E.Auth.Token.001": {
    "name": "TOKEN_MISSING",
    "compact_id": "Xy8Qz",
    "message": "Token missing from Authorization header"
  },
  "E.Auth.Token.018": {
    "name": "TOKEN_EXPIRED",
    "compact_id": "Ab3Cd",
    "message": "Token has expired"
  }
}

8.3 Pure Numeric vs Pure Named vs Hybrid

Pure Numeric:

E.Auth.Token.001
E.Auth.Token.018
E.Auth.Token.003
  • ✅ Simple
  • ✅ Direct hash computation
  • ⚠️ Requires external documentation

Pure Named:

E.Auth.Token.MISSING
E.Auth.Token.EXPIRED
E.Auth.Token.INVALID
  • ✅ Self-documenting
  • ⚠️ Requires resolution mechanism
  • ⚠️ More complex implementation

Hybrid (RECOMMENDED):

Code:    const TOKEN_MISSING = 001;
Display: E.Auth.Token.001
Alias:   TOKEN_MISSING
Catalog: Maps 001 ↔ TOKEN_MISSING
Hash:    Always from 001
  • ✅ Best of both worlds
  • ✅ Flexible display
  • ✅ Stable identity

8.4 Non-Conventional Usage (DISCOURAGED)

While WDP sequence format is normative, sequence semantic conventions are informative. Projects MAY choose not to follow the conventions defined in 7-SEQUENCE-CONVENTIONS.md, though this is DISCOURAGED.

Example of non-conventional usage:

// ❌ DISCOURAGED: Sequential numbering without semantic meaning
E.Auth.Token.001              - Token missing
E.Auth.Token.002              - Token malformed
E.Auth.Token.003              - Token invalid signature
E.Auth.Token.004              - Token expired
E.Auth.Token.005              - Token not yet valid

Recommended conventional alternative:

// ✅ RECOMMENDED: Semantic numbering following conventions
E.Auth.Token.001              - Token missing (001 = MISSING)
E.Auth.Token.002              - Token type mismatch (002 = MISMATCH)
E.Auth.Token.003              - Token invalid signature (003 = INVALID)
E.Auth.Token.018              - Token expired (018 = STALE)
E.Auth.Token.018              - Token not yet valid (018 = STALE)

Why follow conventions?

  • Consistency across projects and organizations
  • Predictable error patterns for developers
  • Easier debugging and error recognition
  • Interoperability between systems

See 7-SEQUENCE-CONVENTIONS.md for complete semantic guidance.

9. Implementation Patterns#

9.1 Pattern A: Code Constants (Recommended)

Rust
// Named constants resolve to numeric values
pub const TOKEN_MISSING: u32 = 001;
pub const TOKEN_EXPIRED: u32 = 018;
pub const TOKEN_INVALID: u32 = 003;

pub fn format_error_code(seq: u32) -> String {
    format!("E.Auth.Token.{:03}", seq)
}

// Usage
let code = format_error_code(TOKEN_MISSING);
// Result: "E.Auth.Token.001"

9.2 Pattern B: Catalog Mapping

Json
{
  "sequences": {
    "001": {
      "name": "TOKEN_MISSING",
      "message": "Token missing from Authorization header"
    },
    "018": {
      "name": "TOKEN_EXPIRED",
      "message": "Token has expired"
    }
  }
}

9.3 Pattern C: Enum with Numeric Values

Rust
#[repr(u32)]
pub enum TokenError {
    Missing = 001,
    Expired = 018,
    Invalid = 003,
}

impl TokenError {
    pub fn to_code(&self) -> String {
        format!("E.Auth.Token.{:03}", *self as u32)
    }
}

// Usage
let code = TokenError::Missing.to_code();
// Result: "E.Auth.Token.001"

9.4 Pattern D: Resolution Function

Rust
pub fn resolve_sequence(seq: &str) -> String {
    match seq {
        "MISSING" | "TOKEN_MISSING" => "001".to_string(),
        "EXPIRED" | "TOKEN_EXPIRED" => "002".to_string(),
        "INVALID" | "TOKEN_INVALID" => "003".to_string(),
        // If already numeric, pass through
        s if s.chars().all(|c| c.is_ascii_digit()) => s.to_string(),
        _ => panic!("Unknown sequence: {}", seq),
    }
}

// Usage
let numeric = resolve_sequence("TOKEN_MISSING");
// Result: "001"

10. Validation Rules#

10.1 Numeric Sequence Validation

Implementations MUST validate that numeric sequences:

  1. Contain exactly 3 ASCII digits
  2. Are within range 001-999 (000 is invalid)
  3. Are zero-padded (e.g., 001, not 1)

Regex:

Regex
^(0[0-9]{2}|[1-9][0-9]{2})$

Explanation:

  • 0[0-9]{2} - 001-099
  • [1-9][0-9]{2} - 100-999

Simplified (if range checking separately):

Regex
^[0-9]{3}$

Then check: 001 ≤ value ≤ 999

10.2 Named Sequence Validation

Implementations MUST validate that named sequences:

  1. Start with uppercase ASCII letter
  2. Contain only uppercase letters, digits, and underscores
  3. Do not start with underscore or digit

Regex:

Regex
^[A-Z][A-Z0-9_]*$

10.3 Validation Examples

Valid Numeric:

001  ✅
042  ✅
100  ✅
999  ✅

Invalid Numeric:

000  ❌ Reserved/invalid
1    ❌ Must be 3 digits (should be 001)
01   ❌ Must be 3 digits (should be 001)
1000 ❌ Exceeds 999
ABC  ❌ Not numeric

Valid Named:

MISSING           ✅
TOKEN_EXPIRED     ✅
NOT_FOUND         ✅
ALREADY_EXISTS    ✅
HTTP_404          ✅

Invalid Named:

missing           ❌ Must be uppercase
Token_Expired     ❌ Must be all uppercase
not-found         ❌ No hyphens
_MISSING          ❌ Cannot start with underscore
404_HTTP          ❌ Cannot start with digit

11. Examples#

11.1 Numeric Sequences

Authentication errors:

E.Auth.Token.001            - Token missing (001 = MISSING)
E.Auth.Token.002            - Token type mismatch (002 = MISMATCH)
E.Auth.Token.003            - Token invalid signature (003 = INVALID)
E.Auth.Token.018            - Token expired (018 = STALE)
E.Auth.Token.031            - Custom token validation error

Database errors:

E.Database.Connection.001   - Connection string missing
E.Database.Connection.017   - Connection timeout
E.Database.Connection.021   - Database not found
E.Database.Connection.027   - Database unavailable

11.2 Named Sequences (Aliases)

Authentication errors:

E.Auth.Token.MISSING        - Alias for 001
E.Auth.Token.MALFORMED      - Alias for 002
E.Auth.Token.INVALID        - Alias for 003
E.Auth.Token.EXPIRED        - Alias for 004
E.Auth.Token.NOT_YET_VALID  - Alias for 005

Database errors:

E.Database.Connection.MISSING       - Alias for 001
E.Database.Connection.TIMEOUT       - Alias for 017
E.Database.Connection.NOT_FOUND     - Alias for 021
E.Database.Connection.UNAVAILABLE   - Alias for 027

11.3 Hash Equivalence

All of these produce the same compact ID:

E.Auth.Token.001            → Hash input: E.AUTH.TOKEN.001 → Xy8Qz
E.Auth.Token.MISSING        → Resolves to 001 → E.AUTH.TOKEN.001 → Xy8Qz
E.Auth.Token.TOKEN_MISSING  → Resolves to 001 → E.AUTH.TOKEN.001 → Xy8Qz
E.Auth.Token.018            → Hash input: E.AUTH.TOKEN.018 → Ab3Cd
E.Auth.Token.EXPIRED        → Resolves to 018 → E.AUTH.TOKEN.018 → Ab3Cd

11.4 Mixed Display Forms

Within the same project:

E.Auth.Token.001            - Numeric (canonical)
E.Parser.Syntax.UNEXPECTED  - Named (alias, resolves to numeric)
E.Database.Query.031        - Numeric (canonical, project-specific)
E.Network.Timeout.017       - Numeric (017 = TIMEOUT, conventional)

All named forms resolve to numeric before hashing.

11.5 Complete Examples with Catalogs

Error Code:

E.Auth.Token.001
E.Auth.Token.018
E.Auth.Token.003

Catalog Entry:

Json
{
  "E.Auth.Token.001": {
    "sequence_name": "TOKEN_MISSING",
    "compact_id": "Xy8Qz",
    "message": "Authentication token missing from request",
    "severity": "E",
    "component": "Auth",
    "primary": "Token",
    "sequence": "001",
    "tone": "negative",
    "priority": 8,
    "blocking": true
  },
  "E.Auth.Token.018": {
    "sequence_name": "TOKEN_EXPIRED",
    "compact_id": "Ab3Cd",
    "message": "Authentication token has expired",
    "severity": "E",
    "component": "Auth",
    "primary": "Token",
    "sequence": "018",
    "tone": "negative",
    "priority": 8,
    "blocking": true
  }
}

Display Options:

Compact:    Xy8Qz
Short:      E.Auth.Token.001
Named:      E.Auth.Token.TOKEN_MISSING
Full:       E.Auth.Token.001 -> Xy8Qz
Verbose:    E.Auth.Token.001 (TOKEN_MISSING) -> Xy8Qz

12. Common Questions (FAQ)#

Q1: How does "versioning" work with sequences?

A: The sequence field doesn't provide traditional API versioning. Instead, it enables catalog evolution - you can add new error codes over time without breaking existing ones.

Example:

Initial release (v1.0):
  E.Auth.Token.001 - Token missing
  E.Auth.Token.002 - Token expired
  E.Auth.Token.003 - Token invalid

Later release (v1.1):
  E.Auth.Token.001 - Token missing        (unchanged)
  E.Auth.Token.002 - Token expired        (unchanged)
  E.Auth.Token.003 - Token invalid        (unchanged)
  E.Auth.Token.004 - Token revoked        (NEW)
  E.Auth.Token.005 - Token blacklisted    (NEW)

Benefits:

  • Existing codes (001-003) remain stable
  • Hash values don't change
  • Client catalogs can be updated incrementally
  • No breaking changes for systems using old codes

Q2: Is the hash input E.AUTH.TOKEN.001 or E.AUTH.TOKEN.1?

A: The hash input is E.AUTH.TOKEN.001 (with zero-padding).

Why it matters:

Sequence 1 → Display: E.Auth.Token.001
            → Hash input: E.AUTH.TOKEN.001  (with 001)
            → Result: Xy8Qz

If you used E.AUTH.TOKEN.1 (no padding):
            → Result: DIFFERENT HASH (wrong!)

Rule: Always use exactly 3 digits with zero-padding in the hash input:

  • 1 → 001
  • 42 → 042
  • 123 → 123

Q3: Do I need to store the zero-padded format?

A: Yes, the canonical format is always zero-padded to 3 digits.

Correct:

Rust
const TOKEN_MISSING: &str = "001";  // Store as "001"
let code = format!("E.Auth.Token.{}", TOKEN_MISSING);
// Result: "E.Auth.Token.001"

Incorrect:

Rust
const TOKEN_MISSING: u32 = 1;  // Don't store as integer 1
let code = format!("E.Auth.Token.{}", TOKEN_MISSING);
// Result: "E.Auth.Token.1" ❌ WRONG!

If you must use integers, always format with padding:

Rust
const TOKEN_MISSING: u32 = 1;
let code = format!("E.Auth.Token.{:03}", TOKEN_MISSING);
// Result: "E.Auth.Token.001" ✅ Correct!

Q4: Can I skip sequences (e.g., 001, 002, 005)?

A: Yes, you can skip sequences. The numbers don't have to be consecutive.

Example:

E.Auth.Token.001 - Token missing
E.Auth.Token.002 - Token expired
E.Auth.Token.010 - Token deprecated (skipped 003-009)
E.Auth.Token.031 - Custom project error (skipped 011-030)

This is useful for:

  • Leaving room for future errors
  • Grouping by semantic ranges (001-010 validation, 011-020 state, etc.)
  • Maintaining consistency with conventions

Q5: What happens if I change a sequence number?

A: Changing a sequence number creates a completely new error code with a different hash.

Example:

Old: E.Auth.Token.001 → Hash: Xy8Qz
New: E.Auth.Token.042 → Hash: DIFFERENT!

→ This is a breaking change
→ Old catalogs won't recognize the new code
→ Logs and monitoring will treat them as separate errors

Best Practice: Never renumber existing sequences. Add new sequences instead.

Q6: Can named sequences contain numbers (e.g., TOKEN_404)?

A: Yes, named sequences can contain digits, but they must start with a letter.

Valid:

TOKEN_MISSING      ✅
TOKEN_404          ✅ (starts with letter)
HTTP_401_ERROR     ✅
OAUTH2_FAILED      ✅

Invalid:

404_TOKEN          ❌ (starts with digit)
_TOKEN_MISSING     ❌ (starts with underscore)

Normative References#

  • STRUCTURE.md - WDP error code structure overview
  • 1-SEVERITY.md - Severity field specification
  • 2-COMPONENT.md - Component field specification
  • 3-PRIMARY.md - Primary field specification
  • 5-COMPACT-IDS.md - Compact ID generation
  • 7-SEQUENCE-CONVENTIONS.md - Sequence conventions (informative)

Informative References#

Conformance#

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.

End of Sequence Field Specification