{
  "id": "dar",
  "set": "DAR",
  "title": "DA Standards — Robotics Software",
  "version": "2026-27",
  "author": "Dan Adiletta",
  "publisher": "Battlesuit LLC",
  "license": {
    "name": "CC BY 4.0",
    "url": "https://creativecommons.org/licenses/by/4.0/",
    "holder": "Battlesuit LLC",
    "attribution": "DA Standards by Battlesuit LLC, CC BY 4.0"
  },
  "url": "https://danadiletta.com/lms/standards/dar/2026-27.json",
  "latest": "https://danadiletta.com/lms/standards/dar.json",
  "about": "Objective text and essential knowledge are this set’s own. Each objective’s aligns lists published standards it maps to, by set and code only; their text is not reproduced.",
  "aligned_to": [
    {
      "id": "ced",
      "title": "AP Computer Science A Course and Exam Description",
      "edition": "2025",
      "publisher": "College Board"
    },
    {
      "id": "csta",
      "title": "CSTA PK–12 Computer Science Standards",
      "edition": "2026, High School Foundational",
      "licence": "CC BY-NC-SA 4.0",
      "url": "https://csteachers.org/pk12standards/"
    },
    {
      "id": "ngss",
      "title": "Next Generation Science Standards",
      "edition": "HS-ETS1 Engineering Design",
      "publisher": "NGSS Lead States",
      "url": "https://www.nextgenscience.org/"
    }
  ],
  "units": [
    {
      "unit": 1,
      "title": "The Command Pattern"
    },
    {
      "unit": 2,
      "title": "Writing a Command"
    },
    {
      "unit": 3,
      "title": "Subsystems and Hardware"
    },
    {
      "unit": 4,
      "title": "Running the Robot"
    },
    {
      "unit": 5,
      "title": "Autonomous Navigation with Pedro"
    },
    {
      "unit": 6,
      "title": "Vision: Where Is That?"
    },
    {
      "unit": 7,
      "title": "Reading the Library You Depend On"
    },
    {
      "unit": 8,
      "title": "Process, the Team, and the Season"
    }
  ],
  "practices": [
    {
      "code": "1",
      "name": "Architect"
    },
    {
      "code": "2",
      "name": "Build"
    },
    {
      "code": "3",
      "name": "Tune"
    },
    {
      "code": "4",
      "name": "Read & Extend"
    },
    {
      "code": "5",
      "name": "Document & Collaborate"
    }
  ],
  "skills": {
    "RB1.A": "Decompose a robot behavior into Commands and Subsystems, deciding what each owns.",
    "RB1.B": "Choose sequential or concurrent command composition for a multi-step behavior, and justify it.",
    "RB2.A": "Implement a Command's lifecycle — constructor, initialize, execute, isFinished, end — correctly.",
    "RB2.B": "Implement a Subsystem that owns its hardware and exposes safe helper methods without blocking.",
    "RB2.C": "Bind a gamepad input to the command it should trigger, using the binding that matches the intended behavior.",
    "RB3.A": "Diagnose a mechanism's real-world behavior against its expected behavior, using live telemetry.",
    "RB3.B": "Carry out a documented tuning procedure in order, verifying one constant before moving to the next.",
    "RB3.C": "Verify a localization system against a physical ground-truth test before trusting it in autonomous.",
    "RB4.A": "Trace a call through Robot, the scheduler, a Subsystem's periodic(), and a Command's lifecycle to explain an observed behavior.",
    "RB4.B": "Extend an unfamiliar or inherited codebase — a library, a teammate's subsystem, a previous year's robot — without breaking what already works.",
    "RB5.A": "Write a commit message, or a disclosure, that names what changed and why, for a teammate on a shared machine.",
    "RB5.B": "Name a hardware constant through the team's shared convention rather than a hardcoded literal."
  },
  "topics": [
    {
      "code": "RB1.1",
      "unit": 1,
      "title": "Blocking vs. Non-Blocking Code",
      "objectives": [
        {
          "code": "RB1.1.A",
          "text": "Explain why a blocking loop stops a robot from doing anything else while it runs, and why an execute()/isFinished() pair does not.",
          "skill": "RB1.A",
          "core": true,
          "evidence": "R",
          "aligns": [
            {
              "set": "ced",
              "code": "2.7"
            }
          ],
          "knowledge": [
            {
              "code": "RB1.1.A.1",
              "text": "A blocking call does not return control to the rest of the program until it finishes."
            },
            {
              "code": "RB1.1.A.2",
              "text": "A robot running one blocking call is a robot that cannot simultaneously do anything else, including read a sensor or answer a button press."
            },
            {
              "code": "RB1.1.A.3",
              "text": "execute() returns immediately every time it's called; the scheduler, not the method, decides when to call it again."
            },
            {
              "code": "RB1.1.A.4",
              "text": "A command's own duration is however many times execute() runs before isFinished() says stop — not a fixed block of time the method occupies."
            }
          ]
        },
        {
          "code": "RB1.1.B",
          "text": "Rewrite a blocking motor-move loop as a Command skeleton — constructor, initialize, execute, isFinished — with no bodies filled in.",
          "skill": "RB2.A",
          "evidence": "A",
          "aligns": [
            {
              "set": "ced",
              "code": "3.1"
            }
          ]
        }
      ]
    },
    {
      "code": "RB1.2",
      "unit": 1,
      "title": "What a Command Owns",
      "objectives": [
        {
          "code": "RB1.2.A",
          "text": "Given a described robot behavior, say what a Command should own and what it should hand off to a Subsystem.",
          "skill": "RB1.A",
          "core": true,
          "evidence": "A",
          "aligns": [
            {
              "set": "ced",
              "code": "3.1"
            }
          ]
        },
        {
          "code": "RB1.2.B",
          "text": "State two reasons separate Command files reduce merge conflicts on a codebase the whole team pushes to.",
          "skill": "RB5.A",
          "evidence": "R",
          "aligns": [
            {
              "set": "csta",
              "code": "HS-SOC-CE-45"
            }
          ]
        }
      ]
    },
    {
      "code": "RB2.1",
      "unit": 2,
      "title": "The Constructor and Requirements",
      "objectives": [
        {
          "code": "RB2.1.A",
          "text": "Write a Command's constructor that declares its subsystem dependency via addRequirements() and stores what execute() will need.",
          "skill": "RB2.A",
          "core": true,
          "evidence": "A",
          "aligns": [
            {
              "set": "ced",
              "code": "3.4"
            }
          ],
          "knowledge": [
            {
              "code": "RB2.1.A.1",
              "text": "The constructor runs once, at robot setup, before any command executes."
            },
            {
              "code": "RB2.1.A.2",
              "text": "addRequirements(subsystem) is what makes the scheduler cancel any other command already using that subsystem."
            },
            {
              "code": "RB2.1.A.3",
              "text": "A command with no declared requirement can run alongside anything, which is correct for some commands and a bug for most."
            },
            {
              "code": "RB2.1.A.4",
              "text": "A timeout stored in the constructor is read later, in isFinished() — declaring it doesn't enforce it by itself."
            }
          ]
        },
        {
          "code": "RB2.1.B",
          "text": "Explain what addRequirements() causes when two commands need the same subsystem at the same time.",
          "skill": "RB4.A",
          "evidence": "R",
          "aligns": []
        }
      ]
    },
    {
      "code": "RB2.2",
      "unit": 2,
      "title": "initialize() and execute()",
      "objectives": [
        {
          "code": "RB2.2.A",
          "text": "Write an execute() method with no blocking calls that does real work every time the scheduler calls it.",
          "skill": "RB2.A",
          "core": true,
          "evidence": "A",
          "aligns": [
            {
              "set": "ced",
              "code": "2.7"
            }
          ]
        },
        {
          "code": "RB2.2.B",
          "text": "Distinguish what belongs in initialize(), which runs once, from what belongs in execute(), which runs until finished.",
          "skill": "RB2.A",
          "evidence": "R",
          "aligns": [
            {
              "set": "ced",
              "code": "3.1"
            }
          ]
        }
      ]
    },
    {
      "code": "RB2.3",
      "unit": 2,
      "title": "isFinished() and end()",
      "objectives": [
        {
          "code": "RB2.3.A",
          "text": "Write an isFinished() condition from a stated exit criterion — elapsed time, a sensor threshold, or an external signal.",
          "skill": "RB2.A",
          "core": true,
          "evidence": "A",
          "aligns": [
            {
              "set": "ced",
              "code": "2.2"
            }
          ],
          "knowledge": [
            {
              "code": "RB2.3.A.1",
              "text": "A time-based exit compares elapsed time, started in initialize(), against a stored duration."
            },
            {
              "code": "RB2.3.A.2",
              "text": "A sensor-based exit compares a live reading against a target the command computed."
            },
            {
              "code": "RB2.3.A.3",
              "text": "An externally-signaled exit reads a flag another command or the driver station set."
            },
            {
              "code": "RB2.3.A.4",
              "text": "isFinished() is checked every scheduler pass — it is not a one-time evaluation."
            }
          ]
        },
        {
          "code": "RB2.3.B",
          "text": "Write an end(boolean interrupted) that leaves hardware safe whether the command finished or was interrupted.",
          "skill": "RB2.A",
          "core": true,
          "evidence": "A",
          "aligns": []
        },
        {
          "code": "RB2.3.C",
          "text": "Give every command that moves the robot a timeout isFinished() enforces, and have end() report whether it arrived, timed out, or was interrupted.",
          "skill": "RB2.A",
          "core": true,
          "evidence": "A",
          "aligns": [],
          "knowledge": [
            {
              "code": "RB2.3.C.1",
              "text": "A command that never quite reaches its tolerance runs until the match ends, and everything queued behind it never runs. Pinned against a wall, a turn is exactly that command."
            },
            {
              "code": "RB2.3.C.2",
              "text": "The timer starts in initialize() and is tested in isFinished() beside the real exit criterion, as an or: arrived, or out of patience."
            },
            {
              "code": "RB2.3.C.3",
              "text": "A timeout is a number the author has to choose: long enough that a working command finishes, short enough that a stuck one is a pause the driver can live with. Artemis's guidance is 3–5 s for a short hop and 10–15 s for a long path."
            },
            {
              "code": "RB2.3.C.4",
              "text": "Because end() runs on every exit, it is where the command says which exit it took. \"TIMED OUT\" in telemetry is what tells a driver the robot did not arrive."
            }
          ]
        },
        {
          "code": "RB2.3.D",
          "text": "Judge a library's movement command against the requirement to give up and leave the robot safe, then decide whether to decorate it or wrap it.",
          "skill": "RB4.B",
          "evidence": "J",
          "aligns": [
            {
              "set": "ngss",
              "code": "HS-ETS1-3"
            }
          ],
          "knowledge": [
            {
              "code": "RB2.3.D.1",
              "text": "SolversLib 0.3.6's TurnCommand and TurnToCommand finish only when the heading is within tolerance. They have no timeout, and they have no end(): they inherit the empty default."
            },
            {
              "code": "RB2.3.D.2",
              "text": "The library's own documentation says so and suggests withTimeout(...). That decorator races the command against a timer and interrupts it when the timer wins, so it ends the command."
            },
            {
              "code": "RB2.3.D.3",
              "text": "Interrupting a command calls its end(true), and this one's end() does nothing. The follower goes on trying to reach that heading — against the wall, at stall — until some later command gives it a new job. A timeout ends a command; cleanup ends the motion. The requirement needs both."
            },
            {
              "code": "RB2.3.D.4",
              "text": "Artemis therefore wraps rather than decorates: its DriveTurnBy and DriveTurnTo extend DriveAbstract, which supplies the timer and a standardCleanup() that stops the follower."
            },
            {
              "code": "RB2.3.D.5",
              "text": "Check the dependency's source, not a description of it. Artemis's own architecture page said SolversLib's turns ended on !follower.isBusy() until it was corrected on 2026-09-19. That was true of 0.3.5 and false of the 0.3.6 the team runs. The conclusion held, but the evidence for it had gone stale."
            }
          ]
        }
      ]
    },
    {
      "code": "RB3.1",
      "unit": 3,
      "title": "What a Subsystem Owns",
      "objectives": [
        {
          "code": "RB3.1.A",
          "text": "Write a Subsystem that declares its hardware from the hardware map and exposes helper methods for its actions.",
          "skill": "RB2.B",
          "core": true,
          "evidence": "A",
          "aligns": [
            {
              "set": "ced",
              "code": "3.3"
            }
          ]
        },
        {
          "code": "RB3.1.B",
          "text": "Explain why two subsystems claiming the same motor is a design error to catch before it runs, not a runtime accident.",
          "skill": "RB1.A",
          "evidence": "R",
          "aligns": []
        },
        {
          "code": "RB3.1.C",
          "text": "Name every hardware reference through the team's one home for hardware names rather than a hardcoded string literal.",
          "skill": "RB5.B",
          "evidence": "A",
          "aligns": [],
          "knowledge": [
            {
              "code": "RB3.1.C.1",
              "text": "The name in code must match the Driver Station's configuration exactly, including case; a mismatch is not a compile error but an exception when the robot is built, naming the device it could not find."
            },
            {
              "code": "RB3.1.C.2",
              "text": "A correctly wired sensor configured under a different name is the most common reason one reports nothing, which is why Artemis's PinpointDoctor looks under the constant's name and under every other."
            },
            {
              "code": "RB3.1.C.3",
              "text": "Typing the name once, in Constants, means a rename is one edit and a typo is impossible to repeat."
            },
            {
              "code": "RB3.1.C.4",
              "text": "Tools that generate configuration (AutoTune) print names as raw strings, and the paste has to be put back on the constants (RB5.4.B)."
            }
          ]
        },
        {
          "code": "RB3.1.D",
          "text": "Put each number in the one home its lifetime calls for — a final constant or a live-editable tunable — and find a number that has two.",
          "skill": "RB5.B",
          "evidence": "A",
          "aligns": [],
          "knowledge": [
            {
              "code": "RB3.1.D.1",
              "text": "A final constant is one the compiler protects: a hardware name or a motor direction. Nothing can change it mid-match, and that is the point."
            },
            {
              "code": "RB3.1.D.2",
              "text": "The dashboard can edit only non-final statics, so a tunable gives up that protection in exchange for adjusting a value while watching the robot respond. That trade is worth making only for a value worth adjusting live."
            },
            {
              "code": "RB3.1.D.3",
              "text": "A dashboard edit does not write the code, and it is gone at the next restart. A value found that way is kept by typing it into the file."
            },
            {
              "code": "RB3.1.D.4",
              "text": "A tunable works only if it is read where it is used, every loop. A dashboard dial that nothing reads looks exactly like one that works."
            },
            {
              "code": "RB3.1.D.5",
              "text": "Artemis shipped exactly that (at e2f2980): Tunables.DRIVE_SPEED was read by nothing, while the speed actually applied came from Constants.DEFAULT_DRIVE_SPEED. One number had two homes, the dashboard one was the dead one, and turning it down for a nervous driver changed nothing, telemetry included. The check that finds it is a search for every reader of the name."
            }
          ]
        }
      ]
    },
    {
      "code": "RB3.2",
      "unit": 3,
      "title": "Reading a Sensor",
      "objectives": [
        {
          "code": "RB3.2.A",
          "text": "Read a sensor value — an encoder, an IMU, an odometry pose, a color reading — and turn it into a decision a command can act on.",
          "skill": "RB2.B",
          "core": true,
          "evidence": "A",
          "aligns": [
            {
              "set": "csta",
              "code": "HS-SYS-HW-29"
            }
          ],
          "knowledge": [
            {
              "code": "RB3.2.A.1",
              "text": "An encoder reports position or velocity, usually as a tick count converted to a real unit."
            },
            {
              "code": "RB3.2.A.2",
              "text": "An IMU reports orientation — heading, and sometimes pitch and roll."
            },
            {
              "code": "RB3.2.A.3",
              "text": "An odometry computer (Artemis's Pinpoint) fuses its own encoders and IMU and reports a pose, so the robot reads a position it did not compute. The follower consumes it (RB5.3)."
            },
            {
              "code": "RB3.2.A.4",
              "text": "A reading is rarely the decision. A distance becomes \"close enough,\" and a color becomes a category, by a threshold the command owns. The sensor does not do that classifying."
            },
            {
              "code": "RB3.2.A.5",
              "text": "A camera reports a detection, already partly processed before the robot sees it. That is the whole of Unit 6."
            }
          ]
        },
        {
          "code": "RB3.2.B",
          "text": "Identify, from a subsystem's field declarations, which sensor reports position, which reports orientation, and which reports proximity or color.",
          "skill": "RB4.A",
          "evidence": "R",
          "aligns": [
            {
              "set": "csta",
              "code": "HS-SYS-HW-29"
            }
          ]
        }
      ]
    },
    {
      "code": "RB3.3",
      "unit": 3,
      "title": "State Inside a Subsystem",
      "objectives": [
        {
          "code": "RB3.3.A",
          "text": "Track a subsystem's internal state with a boolean or enum, and update it correctly inside end(), including on interruption.",
          "skill": "RB2.B",
          "core": true,
          "evidence": "A",
          "aligns": [
            {
              "set": "ced",
              "code": "3.7"
            }
          ]
        },
        {
          "code": "RB3.3.B",
          "text": "Bind an instantaneous hardware action — a servo nudge, a wrist increment — to a button with InstantCommand rather than a full Command.",
          "skill": "RB2.C",
          "evidence": "A",
          "aligns": [
            {
              "set": "ced",
              "code": "1.9"
            }
          ]
        }
      ]
    },
    {
      "code": "RB4.1",
      "unit": 4,
      "title": "OpModes: Two Doors, One Robot",
      "objectives": [
        {
          "code": "RB4.1.A",
          "text": "Explain what a TeleOp OpMode's constructor sets up, what an Autonomous one sets up, and why the parameters each takes are the difference.",
          "skill": "RB4.A",
          "core": true,
          "evidence": "R",
          "aligns": [
            {
              "set": "ced",
              "code": "3.4"
            }
          ]
        },
        {
          "code": "RB4.1.B",
          "text": "Give a subsystem a defaultCommand, and explain what happens to it when another command claims that subsystem and when that command finishes.",
          "skill": "RB1.A",
          "core": true,
          "evidence": "A",
          "aligns": [],
          "knowledge": [
            {
              "code": "RB4.1.B.1",
              "text": "A default command runs whenever no other command requires its subsystem, and its isFinished() returns false forever. It never finishes; it gets interrupted."
            },
            {
              "code": "RB4.1.B.2",
              "text": "Scheduling a command that requires the same subsystem cancels the current owner, which calls that owner's end(true)."
            },
            {
              "code": "RB4.1.B.3",
              "text": "When the new command finishes, the scheduler reschedules the default command at the end of that same pass. That is why the driver never presses anything to get control back."
            },
            {
              "code": "RB4.1.B.4",
              "text": "Artemis's panic button uses exactly this: new InstantCommand(() -> drive.stop(), drive). Requiring drive is the trick. It cancels whatever owns the wheels, the stop runs once, and Drive picks them straight back up."
            },
            {
              "code": "RB4.1.B.5",
              "text": "Without the requirement, the stop still runs and the robot stops where it is, but the path command still owns the wheels, so Drive is not running. The driver has no joystick until that command's own timeout (RB2.3.C) lets go."
            },
            {
              "code": "RB4.1.B.6",
              "text": "Artemis's Drive holds its spot when the sticks are still: after Tunables.HOLD_DELAY_MS it calls holdCurrent() once, and a robot pushed off that spot drives back. Any stick takes the wheels straight back, because follower.manual() drops a hold."
            },
            {
              "code": "RB4.1.B.7",
              "text": "It brakes at zero for the delay first, because holdCurrent() remembers the pose at the moment it is called, and a robot still coasting would be dragged back to where the sticks were let go. The delay is a timer, not a count of loop passes, so it does not change when the loop slows."
            },
            {
              "code": "RB4.1.B.8",
              "text": "setDefaultCommand() is handed one object, and the scheduler resumes that same object every time, so initialize() has to clear whatever the last run left in its fields. Iapetus's first version did not, and after any button command it stopped holding until a stick moved."
            }
          ]
        },
        {
          "code": "RB4.1.C",
          "text": "Explain why a running robot flushes telemetry from one place, once per loop, and what a second flush does.",
          "skill": "RB4.A",
          "evidence": "R",
          "aligns": [],
          "knowledge": [
            {
              "code": "RB4.1.C.1",
              "text": "Every part of the robot adds telemetry. Only Sensors.periodic() flushes it, once per scheduler loop."
            },
            {
              "code": "RB4.1.C.2",
              "text": "A flush sends what has been added and clears it, so a second flush in the same loop splits that loop's report in two. The display shows one part at a time, and nothing reports an error."
            },
            {
              "code": "RB4.1.C.3",
              "text": "The rule is about the running loop, not about the word update() appearing once in the project. An OpMode flushing its own init prompt before the robot is built conflicts with nothing. Artemis's own comment claimed the stricter version until the exceptions were found on 2026-09-19. A rule stated more broadly than it is true teaches students to distrust it the first time they find an exception."
            },
            {
              "code": "RB4.1.C.4",
              "text": "The same shape recurs with follower.update() (RB5.3.B): state that advances once per loop has exactly one owner, and calling it twice is not twice as good."
            }
          ]
        }
      ]
    },
    {
      "code": "RB4.2",
      "unit": 4,
      "title": "Binding Buttons to Behavior",
      "objectives": [
        {
          "code": "RB4.2.A",
          "text": "Choose on-press, on-release, or while-held for a described driver action, and justify the choice against the other two.",
          "skill": "RB2.C",
          "core": true,
          "evidence": "J",
          "aligns": [],
          "knowledge": [
            {
              "code": "RB4.2.A.1",
              "text": "On-press fires once, at the moment of contact — for an action that should happen exactly once per press, like a toggle."
            },
            {
              "code": "RB4.2.A.2",
              "text": "On-release fires once, at the moment of release — for an action tied to letting go, like closing a claw."
            },
            {
              "code": "RB4.2.A.3",
              "text": "While-held fires repeatedly for as long as the button stays down — for a continuous or incremental action, like raising an arm."
            },
            {
              "code": "RB4.2.A.4",
              "text": "The wrong binding for the action is a real, common bug: a toggle bound while-held fires many times per press instead of once."
            }
          ]
        },
        {
          "code": "RB4.2.B",
          "text": "Read a robot's button-binding block and state what a specific button does, without running the code.",
          "skill": "RB4.A",
          "evidence": "R",
          "aligns": [
            {
              "set": "ced",
              "code": "1.10"
            }
          ]
        }
      ]
    },
    {
      "code": "RB4.3",
      "unit": 4,
      "title": "Sequencing an Autonomous",
      "objectives": [
        {
          "code": "RB4.3.A",
          "text": "Sequence a multi-step autonomous routine with a SequentialCommandGroup, and state which parts run in order and which run concurrently.",
          "skill": "RB1.B",
          "core": true,
          "evidence": "A",
          "aligns": [
            {
              "set": "ced",
              "code": "2.1"
            }
          ]
        },
        {
          "code": "RB4.3.B",
          "text": "Choose sequential or concurrent composition for a described autonomous behavior, and defend the choice against its tradeoff.",
          "skill": "RB1.B",
          "evidence": "J",
          "aligns": [
            {
              "set": "ngss",
              "code": "HS-ETS1-3"
            }
          ]
        }
      ]
    },
    {
      "code": "RB5.1",
      "unit": 5,
      "title": "The Field Coordinate System",
      "objectives": [
        {
          "code": "RB5.1.A",
          "text": "Convert a heading between degrees and radians when setting a starting pose, and state what happens if the conversion is skipped.",
          "skill": "RB2.A",
          "core": true,
          "evidence": "A",
          "aligns": []
        },
        {
          "code": "RB5.1.B",
          "text": "State which direction the robot faces at 0, π/2, π, and 3π/2 radians in the team's own coordinate convention.",
          "skill": "RB4.A",
          "evidence": "R",
          "aligns": [],
          "knowledge": [
            {
              "code": "RB5.1.B.1",
              "text": "0 radians faces the positive-X direction — right on the field map, not up."
            },
            {
              "code": "RB5.1.B.2",
              "text": "π/2 radians faces positive-Y, up."
            },
            {
              "code": "RB5.1.B.3",
              "text": "π radians faces negative-X, left."
            },
            {
              "code": "RB5.1.B.4",
              "text": "3π/2 radians faces negative-Y, down."
            },
            {
              "code": "RB5.1.B.5",
              "text": "The dashboard does not share this frame, and that is the trap. The follower's frame has its origin at a field corner; the dashboard's canvas has its origin at the field center, so bridging them is a shift, a rotation and a mirror — not a nudge."
            },
            {
              "code": "RB5.1.B.6",
              "text": "The bridge is a named preset, and it is selected in code precisely so a dropdown in the dashboard UI cannot silently disagree with the robot."
            },
            {
              "code": "RB5.1.B.7",
              "text": "A frame picked wrongly still reads perfectly consistently with itself; the numbers simply mean something else. Nothing errors, which is why this is found by checking rather than by waiting for a failure."
            }
          ]
        },
        {
          "code": "RB5.1.C",
          "text": "Confirm from the dashboard's frame markers, without moving the robot, that the drawing frame agrees with the driving frame — and stop when it does not.",
          "skill": "RB3.C",
          "core": true,
          "evidence": "A",
          "aligns": [
            {
              "set": "ngss",
              "code": "HS-ETS1-4"
            }
          ],
          "knowledge": [
            {
              "code": "RB5.1.C.1",
              "text": "The drawing shows the origin, both axis directions, the center and the far corner, so each is falsifiable by eye."
            },
            {
              "code": "RB5.1.C.2",
              "text": "The origin marker belongs in a corner; seeing it anywhere else means the frame is wrong and every coordinate written from that point on is fiction."
            },
            {
              "code": "RB5.1.C.3",
              "text": "The two axis arms must point the way the convention says — an axis drawn the wrong way is the mirror half of a preset mismatch."
            },
            {
              "code": "RB5.1.C.4",
              "text": "This check needs no robot movement, which is what makes it the first thing to do rather than the last."
            },
            {
              "code": "RB5.1.C.5",
              "text": "If telemetry numbers move while the drawing does not, the fault is in the drawing, not in localization — two different problems that present as one complaint."
            }
          ]
        },
        {
          "code": "RB5.1.D",
          "text": "Name where an outside coordinate is converted into the robot's frame, and why converting inside a command is a defect though the number is right.",
          "skill": "RB4.A",
          "core": true,
          "evidence": "R",
          "aligns": []
        }
      ]
    },
    {
      "code": "RB5.2",
      "unit": 5,
      "title": "Commands That Drive a Path",
      "objectives": [
        {
          "code": "RB5.2.A",
          "text": "Build an autonomous Command's path from the current pose, and have isFinished() test measured heading or position — not a timer, count, or busy flag.",
          "skill": "RB2.A",
          "core": true,
          "evidence": "A",
          "aligns": [
            {
              "set": "ced",
              "code": "3.1"
            }
          ]
        },
        {
          "code": "RB5.2.B",
          "text": "Explain why a Pedro-driven command's execute() is usually empty, and trace where the actual driving happens instead.",
          "skill": "RB4.A",
          "evidence": "R",
          "aligns": []
        },
        {
          "code": "RB5.2.C",
          "text": "Hand control back to manual driving by stopping the follower, and explain why zeroing the drive motors behind its back is worse than doing nothing.",
          "skill": "RB2.A",
          "core": true,
          "evidence": "A",
          "aligns": [],
          "knowledge": [
            {
              "code": "RB5.2.C.1",
              "text": "Handing control back means telling the follower to stop, not telling the motors to stop — the follower is what is still issuing commands."
            },
            {
              "code": "RB5.2.C.2",
              "text": "The follower owns the drive motors and caches the last power it wrote to each wheel, skipping any write that matches what is already there."
            },
            {
              "code": "RB5.2.C.3",
              "text": "So zeroing the motors behind its back leaves that cache disagreeing with the hardware, and the next command that happens to ask for the cached value is silently dropped — no error, and a mechanism that simply does not move."
            },
            {
              "code": "RB5.2.C.4",
              "text": "The general rule this is a case of: when a library owns a resource, reaching around it to touch that resource directly does not bypass the library, it corrupts the library's idea of the world."
            }
          ]
        }
      ]
    },
    {
      "code": "RB5.3",
      "unit": 5,
      "title": "Verifying Localization",
      "objectives": [
        {
          "code": "RB5.3.A",
          "text": "Run the push/rotate/strafe/spin verification sequence on a drivetrain, and say which physical cause a failed test points to.",
          "skill": "RB3.C",
          "core": true,
          "evidence": "A",
          "aligns": [
            {
              "set": "ngss",
              "code": "HS-ETS1-3"
            }
          ],
          "knowledge": [
            {
              "code": "RB5.3.A.1",
              "text": "A push test isolates whether translation is tracked correctly — the reported distance should match the physical distance pushed."
            },
            {
              "code": "RB5.3.A.2",
              "text": "A rotation test isolates whether heading is tracked correctly — turning 90° by hand should move the reported heading by 90°."
            },
            {
              "code": "RB5.3.A.3",
              "text": "A strafe test, on a mecanum drivetrain, isolates whether X and Y are tracked independently rather than coupled."
            },
            {
              "code": "RB5.3.A.4",
              "text": "A spin test, turning the robot in place and setting it back, isolates whether the pod offsets are right: the reported position should come back to where it started rather than wander."
            },
            {
              "code": "RB5.3.A.5",
              "text": "Each test is designed to isolate one failure mode — a push-test failure and a spin-test failure point at different physical causes even though both would show up as \"the robot doesn't go where it's told.\""
            }
          ]
        },
        {
          "code": "RB5.3.B",
          "text": "Explain why follower.update() runs exactly once per scheduler loop, in one place: skipped, nothing moves; called twice, the second call plans from a gap of almost nothing, which a dead-wheel localizer turns into a noisy velocity.",
          "skill": "RB3.A",
          "evidence": "R",
          "aligns": []
        }
      ]
    },
    {
      "code": "RB5.4",
      "unit": 5,
      "title": "Tuning the Follower",
      "objectives": [
        {
          "code": "RB5.4.A",
          "text": "Carry out the follower's tuning sequence in order, redeploying each stage's result before the next, and explain why tuning above an untuned stage measures noise.",
          "skill": "RB3.B",
          "core": true,
          "evidence": "A",
          "aligns": [
            {
              "set": "ngss",
              "code": "HS-ETS1-4"
            }
          ],
          "knowledge": [
            {
              "code": "RB5.4.A.1",
              "text": "The sequence is ordered by dependency, not by preference: each stage is measured on top of the configuration the stages above it produced."
            },
            {
              "code": "RB5.4.A.2",
              "text": "Drivetrain geometry and wheel directions come first, because every later measurement assumes the wheels turn the way the diagram says."
            },
            {
              "code": "RB5.4.A.3",
              "text": "Localization is tuned next, and the hand tests gate it — a later stage tuned on top of localization that is lying is tuned against noise, which is the whole reason the gate exists rather than being advice."
            },
            {
              "code": "RB5.4.A.4",
              "text": "The follower's own behavioral numbers come after that, because the procedure measures how the robot coasts and brakes and then plans from those measurements."
            },
            {
              "code": "RB5.4.A.5",
              "text": "A confirmation stage at the end proves the result rather than assuming it."
            },
            {
              "code": "RB5.4.A.6",
              "text": "Each stage's result must be pasted into the configuration and redeployed before the next stage runs, because the next stage reads the config off the running robot, not off the editor."
            }
          ]
        },
        {
          "code": "RB5.4.B",
          "text": "Tell the tuner's numbers from the team's in a pasted tuner result, and restore the team's after a paste.",
          "skill": "RB5.B",
          "evidence": "A",
          "aligns": [],
          "knowledge": [
            {
              "code": "RB5.4.B.1",
              "text": "A tuner writes the numbers it measured; it does not know which values in the same block were the team's deliberate choices."
            },
            {
              "code": "RB5.4.B.2",
              "text": "Pasting a tuner's whole block over a hand-maintained one therefore silently reverts anything the tuner does not produce."
            },
            {
              "code": "RB5.4.B.3",
              "text": "This team has lost the path end constraints to exactly that paste, in two repositories, after the warning was written down — so the lesson is that a warning in a document does not survive a copy-paste, and the check has to happen at the paste."
            },
            {
              "code": "RB5.4.B.4",
              "text": "A tuner also prints hardware names as raw strings, which must be replaced with the shared constants so a name is still typed in exactly one place (RB5.B)."
            }
          ]
        }
      ]
    },
    {
      "code": "RB6.1",
      "unit": 6,
      "title": "Tracking a Target",
      "objectives": [
        {
          "code": "RB6.1.A",
          "text": "Read a target's range and bearing from the subsystem that owns the camera, and handle null without waiting for a good reading inside execute().",
          "skill": "RB2.A",
          "core": true,
          "evidence": "A",
          "aligns": [
            {
              "set": "csta",
              "code": "HS-SYS-HW-29"
            }
          ],
          "knowledge": [
            {
              "code": "RB6.1.A.1",
              "text": "One subsystem owns the camera (Sensors); commands ask it rather than reading the camera themselves, the same one-owner shape as telemetry (RB4.1.C)."
            },
            {
              "code": "RB6.1.A.2",
              "text": "A target's range() is flat-floor inches from the robot's center and bearing() is radians to turn, positive to the left, the same sign as every heading in the frame."
            },
            {
              "code": "RB6.1.A.3",
              "text": "null means three different things on purpose: no camera, nothing in view, and seen too long ago. The robot has to cope with all three, so a command cannot tell them apart and should not need to."
            },
            {
              "code": "RB6.1.A.4",
              "text": "A command that aims holds its last target when the answer is null and asks again next loop. Waiting for a good reading inside execute() is the blocking loop of RB1.1.A again."
            }
          ]
        },
        {
          "code": "RB6.1.B",
          "text": "Explain why a sighting is stored in field coordinates rather than robot-relative, and name what makes a stored one wrong and what does not.",
          "skill": "RB4.A",
          "evidence": "R",
          "aligns": [],
          "knowledge": [
            {
              "code": "RB6.1.B.1",
              "text": "The camera reports a tag relative to the robot; odometry puts it on the field; the field position is what is stored."
            },
            {
              "code": "RB6.1.B.2",
              "text": "A field position stays true while the robot drives and turns, so a tag an arm blocks for half a second is still where it was left. Every query re-measures it from where the robot is now."
            },
            {
              "code": "RB6.1.B.3",
              "text": "What makes a stored sighting wrong is the object moving, and a memory limit (TAG_MEMORY_MS) is the only defense, so it trades riding out a blocked view against reacting to a moved object."
            },
            {
              "code": "RB6.1.B.4",
              "text": "A sighting is only as good as the odometry that placed it: a localization error becomes a target error."
            }
          ]
        },
        {
          "code": "RB6.1.C",
          "text": "State how exact a merged target is from how many tags were in view, and name the point it estimates.",
          "skill": "RB3.A",
          "evidence": "R",
          "aligns": [],
          "knowledge": [
            {
              "code": "RB6.1.C.1",
              "text": "Tags on one object are merged into one target: the average of the tags in view, and tagCount says how many that was."
            },
            {
              "code": "RB6.1.C.2",
              "text": "One end tag alone is off by however far that tag sits from the middle of the set."
            },
            {
              "code": "RB6.1.C.3",
              "text": "Every tag in view gives the center of the tags, which is the object's own center only if the tags sit evenly around it."
            },
            {
              "code": "RB6.1.C.4",
              "text": "BIOBUZZ's Hives are the case where they don't: the SDK places every Hive tag 7.19″ and −5.62″ off the cluster's origin on its other two axes, so four tags in view still land about 9″ from that point. Artemis and Iapetus both said \"the true centre\" until 2026-09-19."
            }
          ]
        }
      ]
    },
    {
      "code": "RB6.2",
      "unit": 6,
      "title": "Aiming From the Right Place and Time",
      "objectives": [
        {
          "code": "RB6.2.A",
          "text": "Compute the angles and reach a mechanism needs from a pivot off the robot's center, and set its target from them every loop.",
          "skill": "RB2.A",
          "core": true,
          "evidence": "A",
          "aligns": [],
          "knowledge": [
            {
              "code": "RB6.2.A.1",
              "text": "A turret or arm does not pivot at the robot's center, so the robot's bearing to a target is the wrong angle for the mechanism."
            },
            {
              "code": "RB6.2.A.2",
              "text": "Subtract the pivot's position from the target's, in the robot frame, before taking angles: atan2(left − pivotLeft, forward − pivotForward) to swing, atan2(up − pivotUp, flat distance) to tilt, and the hypotenuse for reach."
            },
            {
              "code": "RB6.2.A.3",
              "text": "The pivot's position is a measured number and belongs in Constants (RB3.1.D), not typed into the command."
            },
            {
              "code": "RB6.2.A.4",
              "text": "Read the angles in execute() every loop and set the mechanism's target from them; the mechanism follows the target, and the target follows the object."
            }
          ]
        },
        {
          "code": "RB6.2.B",
          "text": "Explain why a frame's tags use the pose from when it was taken, and estimate the error the current pose causes during a turn.",
          "skill": "RB3.A",
          "evidence": "R",
          "aligns": [],
          "knowledge": [
            {
              "code": "RB6.2.B.1",
              "text": "A camera frame is old by the time the robot reads it — Artemis accepts frames up to 200 ms old."
            },
            {
              "code": "RB6.2.B.2",
              "text": "Placing the frame's tags with the pose from now rotates every one of them by however far the robot turned in that time."
            },
            {
              "code": "RB6.2.B.3",
              "text": "The size is estimable: turning at 2 rad/s over 40 ms is about 0.08 rad (≈5°), which moves a tag 48″ away by about 4″. That is enough to miss."
            },
            {
              "code": "RB6.2.B.4",
              "text": "The fix is to keep recent poses with timestamps and look up the one from when the frame was taken. A trail spaced by distance cannot do it, because a turn in place drops no points. Artemis placed tags from the current pose until 2026-09-19, and it now keeps half a second of timestamped poses."
            }
          ]
        }
      ]
    },
    {
      "code": "RB6.3",
      "unit": 6,
      "title": "Localization: Where Am I?",
      "objectives": [
        {
          "code": "RB6.3.A",
          "text": "Distinguish tracking from localization by which way the trust runs: odometry vouching for a tag, or a tag for the robot.",
          "skill": "RB4.A",
          "evidence": "R",
          "aligns": [],
          "knowledge": [
            {
              "code": "RB6.3.A.1",
              "text": "Tracking trusts odometry to say where the tag is, which is the only option when the tag rides on something that moves."
            },
            {
              "code": "RB6.3.A.2",
              "text": "Localization trusts a fixed tag to say where the robot is."
            },
            {
              "code": "RB6.3.A.3",
              "text": "Same camera, same detections, opposite direction of trust, and the direction decides which errors spread to what."
            }
          ]
        },
        {
          "code": "RB6.3.B",
          "text": "Decide whether a game's tags may correct odometry, and defend leaving that off until the tags are shown to stay put.",
          "skill": "RB3.C",
          "evidence": "J",
          "aligns": [
            {
              "set": "ngss",
              "code": "HS-ETS1-3"
            }
          ],
          "knowledge": [
            {
              "code": "RB6.3.B.1",
              "text": "A tag on a moved object reads exactly as confidently as a fixed one; nothing in the detection says it moved."
            },
            {
              "code": "RB6.3.B.2",
              "text": "Letting it correct odometry therefore drags the robot's idea of itself toward where the object used to be, with no error anywhere."
            },
            {
              "code": "RB6.3.B.3",
              "text": "So correction is off until proven: report the vision pose without applying it, compare it against a measured position, and turn correction on only for tags shown to stay put."
            },
            {
              "code": "RB6.3.B.4",
              "text": "A policy still filters what it accepts (fresh, close, on the field, not an implausible jump) and reports every rejection with its reason."
            },
            {
              "code": "RB6.3.B.5",
              "text": "The camera needs the robot's heading in its own frame. Artemis handed it Pedro's, 90° off, until 2026-09-19: one more conversion that belongs at the edge (RB5.1.D)."
            }
          ]
        }
      ]
    },
    {
      "code": "RB7.1",
      "unit": 7,
      "title": "What the Scheduler and the OpMode Base Class Actually Do",
      "objectives": [
        {
          "code": "RB7.1.A",
          "text": "Trace one iteration of the scheduler loop — run(), periodic(), execute(), isFinished(), end() — to explain an observed behavior, and say which of those classes are yours.",
          "skill": "RB4.A",
          "evidence": "R",
          "aligns": [
            {
              "set": "ced",
              "code": "1.10"
            }
          ]
        },
        {
          "code": "RB7.1.B",
          "text": "Read what reset() does in the library's source, say how that differs from its documentation, and name a consequence for hardware left mid-motion.",
          "skill": "RB4.B",
          "evidence": "J",
          "aligns": [
            {
              "set": "csta",
              "code": "HS-PRO-RD-17"
            }
          ],
          "knowledge": [
            {
              "code": "RB7.1.B.1",
              "text": "The library's reset() is documented as \"cancels all previous commands.\""
            },
            {
              "code": "RB7.1.B.2",
              "text": "What it actually does is discard the scheduler singleton, so the next request for a scheduler builds an empty one."
            },
            {
              "code": "RB7.1.B.3",
              "text": "Those are not the same thing: cancelling a command runs its end(true); discarding the scheduler runs nothing at all."
            },
            {
              "code": "RB7.1.B.4",
              "text": "So a mechanism that was mid-motion when reset() was called stays exactly as it was — the safe-state cleanup RB2.3.B requires you to write is real code that, on this path, is never called."
            },
            {
              "code": "RB7.1.B.5",
              "text": "The method that does what the documentation describes is a different method, the one that cancels each scheduled command in turn."
            },
            {
              "code": "RB7.1.B.6",
              "text": "Discarding the scheduler also drops every subsystem registration and default command, because those are held on the instance — which is correct at teardown and a bug mid-match."
            },
            {
              "code": "RB7.1.B.7",
              "text": "The general lesson, and the reason this objective is judgment rather than recall: a docstring is a claim, and the source is the evidence. This one was found by reading the library, not by trusting it."
            }
          ]
        },
        {
          "code": "RB7.1.C",
          "text": "Name where the OpMode base class guarantees teardown runs, and why a hook that saves state must sit there rather than in the last command.",
          "skill": "RB4.A",
          "evidence": "R",
          "aligns": []
        }
      ]
    },
    {
      "code": "RB8.1",
      "unit": 8,
      "title": "Working on a Shared Codebase",
      "objectives": [
        {
          "code": "RB8.1.A",
          "text": "Write a commit message naming what changed and why, for the teammate who sits down at the shared laptop next.",
          "skill": "RB5.A",
          "core": true,
          "evidence": "A",
          "aligns": [
            {
              "set": "csta",
              "code": "HS-PRO-PD-15"
            }
          ],
          "knowledge": [
            {
              "code": "RB8.1.A.1",
              "text": "A commit message names what changed, in enough detail that a diff isn't required to know."
            },
            {
              "code": "RB8.1.A.2",
              "text": "A useful commit message also names why, when the reason isn't obvious from the change alone."
            },
            {
              "code": "RB8.1.A.3",
              "text": "\"Fix stuff\" or \"updates\" describes every commit ever made and therefore describes none of them."
            },
            {
              "code": "RB8.1.A.4",
              "text": "On a shared laptop, the next person's first move is reading the last few commit messages to know what state the code is in — which is the actual audience a commit message is written for."
            }
          ]
        },
        {
          "code": "RB8.1.B",
          "text": "Pull before starting work, and explain what happens to a shared laptop's code if you don't and a teammate already pushed.",
          "skill": "RB5.A",
          "evidence": "R",
          "aligns": [
            {
              "set": "csta",
              "code": "HS-PRO-PD-13"
            }
          ]
        }
      ]
    },
    {
      "code": "RB8.2",
      "unit": 8,
      "title": "Reading Documentation You Didn't Write",
      "objectives": [
        {
          "code": "RB8.2.A",
          "text": "Use a third-party library's documentation to accomplish a task the docs don't demonstrate directly.",
          "skill": "RB4.B",
          "core": true,
          "evidence": "A",
          "aligns": [
            {
              "set": "ced",
              "code": "1.7"
            },
            {
              "set": "csta",
              "code": "HS-PRO-PD-13"
            }
          ]
        },
        {
          "code": "RB8.2.B",
          "text": "Extend a subsystem or command written by a previous season's team without breaking its existing behavior.",
          "skill": "RB4.B",
          "core": true,
          "evidence": "A",
          "aligns": [
            {
              "set": "csta",
              "code": "HS-PRO-RD-17"
            }
          ]
        }
      ]
    },
    {
      "code": "RB8.3",
      "unit": 8,
      "title": "Evaluating Generated or Borrowed Code",
      "objectives": [
        {
          "code": "RB8.3.A",
          "text": "Evaluate AI-generated or borrowed Java code against the requirement it was meant to meet, and identify what it got wrong.",
          "skill": "RB4.B",
          "core": true,
          "evidence": "A",
          "aligns": [
            {
              "set": "csta",
              "code": "HS-PRO-RD-18"
            },
            {
              "set": "csta",
              "code": "HS-ALG-PS-05"
            }
          ]
        },
        {
          "code": "RB8.3.B",
          "text": "Disclose which parts of a submission were AI-generated or adapted from another team's public code, and cite the source.",
          "skill": "RB5.A",
          "evidence": "A",
          "aligns": [
            {
              "set": "csta",
              "code": "HS-PRO-PD-14"
            }
          ]
        }
      ]
    },
    {
      "code": "RB8.4",
      "unit": 8,
      "title": "The Season as a Design Cycle",
      "objectives": [
        {
          "code": "RB8.4.A",
          "text": "State a specific criterion a mechanism must satisfy — a cycle time, a tolerance, a weight budget — before it gets built.",
          "skill": "RB1.A",
          "core": true,
          "evidence": "A",
          "aligns": [
            {
              "set": "ngss",
              "code": "HS-ETS1-1"
            }
          ]
        },
        {
          "code": "RB8.4.B",
          "text": "Compare two design solutions to the same problem against the season's constraints, and justify which one the team should build.",
          "skill": "RB1.B",
          "evidence": "J",
          "aligns": [
            {
              "set": "ngss",
              "code": "HS-ETS1-2"
            },
            {
              "set": "ngss",
              "code": "HS-ETS1-3"
            }
          ]
        }
      ]
    },
    {
      "code": "RB8.5",
      "unit": 8,
      "title": "Debugging as a Discipline",
      "objectives": [
        {
          "code": "RB8.5.A",
          "text": "Isolate a fault one layer at a time from the sensor outward, starting with a test that touches only the sensor, and name the layer.",
          "skill": "RB3.A",
          "core": true,
          "evidence": "A",
          "aligns": [
            {
              "set": "ngss",
              "code": "HS-ETS1-2"
            }
          ],
          "knowledge": [
            {
              "code": "RB8.5.A.1",
              "text": "Frozen dashboard coordinates, a tuner that never notices the robot moving, and an autonomous that drives as if it never moved all mean one thing: position data isn't arriving. Four layers can produce that same symptom."
            },
            {
              "code": "RB8.5.A.2",
              "text": "Start with a test that touches nothing but the sensor. Artemis's PinpointDoctor talks to the device directly, with no Pedro, no SolversLib and none of the team's code, because that isolation is the point. Push the robot by hand and watch the raw ticks."
            },
            {
              "code": "RB8.5.A.3",
              "text": "Each step rules out a whole layer: the sensor, then configuration, then directions and frame, then version skew, then the team's own code. Skipping ahead means debugging a layer that might be fine. Once the sensor is proven, stop blaming the wiring."
            },
            {
              "code": "RB8.5.A.4",
              "text": "Compare configuration against a known-good control, and check that the control still matches. Artemis's guide said its pod offsets matched last season's robot \"exactly\" for two days after a re-tune had flipped both signs, until 2026-09-19."
            },
            {
              "code": "RB8.5.A.5",
              "text": "Suspect the team's code last. And if telemetry moves while the drawing does not, that is a drawing problem, not a localization one."
            }
          ]
        },
        {
          "code": "RB8.5.B",
          "text": "Write an issue-log entry for an hour lost: what broke, the root cause, and the fix — dated, and where the next person will look.",
          "skill": "RB5.A",
          "core": true,
          "evidence": "A",
          "aligns": [
            {
              "set": "csta",
              "code": "HS-PRO-PD-15"
            }
          ],
          "knowledge": [
            {
              "code": "RB8.5.B.1",
              "text": "The rule is Artemis's own: \"Add one when you lose an hour to something.\" A fix made silently teaches the next person nothing."
            },
            {
              "code": "RB8.5.B.2",
              "text": "Write the root cause, not the action. The template says it outright: \"'Reinstalled Android Studio' is not a root cause.\""
            },
            {
              "code": "RB8.5.B.3",
              "text": "When the cause is outside the team's code, such as a version seam, the entry is written before code gets edited to work around it."
            },
            {
              "code": "RB8.5.B.4",
              "text": "Date it. The log is read by the next person at the laptop, which is the same audience as a commit message (RB8.1.A)."
            }
          ]
        },
        {
          "code": "RB8.5.C",
          "text": "Retract a lead in writing when evidence clears it, naming the evidence, and date any comparison against a known-good control.",
          "skill": "RB3.A",
          "evidence": "J",
          "aligns": [],
          "knowledge": [
            {
              "code": "RB8.5.C.1",
              "text": "A lead is a hypothesis, and clearing one is a result worth writing down, so nobody chases it again."
            },
            {
              "code": "RB8.5.C.2",
              "text": "Artemis's example: a programmer flagged the pod type (encoderResolution) as suspect, then retracted it after diffing the Pinpoint block against last season's robot, which had localized all season."
            },
            {
              "code": "RB8.5.C.3",
              "text": "That same entry called the two configs \"byte-for-byte identical\". Nine days later a re-tune changed the offsets, and the comparison quietly stopped being true. A comparison against a control is a fact about a date, so write the date with it."
            }
          ]
        },
        {
          "code": "RB8.5.D",
          "text": "Judge a checking tool's verdicts: tell a false alarm from a false all-clear, say which does more harm, and make a skipped check say so.",
          "skill": "RB4.B",
          "evidence": "J",
          "aligns": [
            {
              "set": "ngss",
              "code": "HS-ETS1-3"
            }
          ],
          "knowledge": [
            {
              "code": "RB8.5.D.1",
              "text": "A checker can be wrong two ways: a false alarm (it flags what is fine) and a false all-clear (it passes what it never examined)."
            },
            {
              "code": "RB8.5.D.2",
              "text": "A false alarm costs trust. Artemis's structure check once flagged 13 files, all fine, because it was comparing against the wrong repository — and \"a checker that cries wolf is worse than no checker\" because people learn to ignore it."
            },
            {
              "code": "RB8.5.D.3",
              "text": "A false all-clear is worse where it is believed, because nothing prompts a second look."
            },
            {
              "code": "RB8.5.D.4",
              "text": "The two met in one script: the 2026-09-08 fix for the false alarm made the check skip when the comparison repo was wrong, and the skip then printed \"✓ Clean\". Every fresh clone, which has no comparison repo at all, got that all-clear. Found and fixed on 2026-09-19: a skipped check now says the main check didn't run and exits with its own code."
            },
            {
              "code": "RB8.5.D.5",
              "text": "So a checker has three verdicts, not two: passed, failed, and couldn't check. And it is trusted only after every branch has been driven, the boring ones included."
            }
          ]
        }
      ]
    },
    {
      "code": "RB8.6",
      "unit": 8,
      "title": "Upstream and Dependency",
      "objectives": [
        {
          "code": "RB8.6.A",
          "text": "Distinguish an upstream you merge from a dependency you version-bump, and say which of the robot's parts is which.",
          "skill": "RB4.B",
          "evidence": "R",
          "aligns": [],
          "knowledge": [
            {
              "code": "RB8.6.A.1",
              "text": "The upstream is FIRST's FtcRobotController, the SDK itself, pulled with a git merge once a season."
            },
            {
              "code": "RB8.6.A.2",
              "text": "The dependencies (SolversLib, Pedro Pathing, Panels) are version numbers in TeamCode/build.gradle, changed by editing a number. There is nothing to merge."
            },
            {
              "code": "RB8.6.A.3",
              "text": "Merging a dependency, or forking one to get a robot project, is the category error. Artemis's comparison: \"like forking React to get a website.\""
            },
            {
              "code": "RB8.6.A.4",
              "text": "Artemis deliberately leaves out SolversLib's pedroPathing module, which is compiled against one Pedro major. Its own drive commands talk to Pedro, so the two libraries can move independently."
            }
          ]
        },
        {
          "code": "RB8.6.B",
          "text": "Update one dependency at a time, building in between, and treat a major version as a port rather than a bump.",
          "skill": "RB4.B",
          "evidence": "A",
          "aligns": [],
          "knowledge": [
            {
              "code": "RB8.6.B.1",
              "text": "Bump one dependency, build, then the next. When something breaks, you want to know which one did it."
            },
            {
              "code": "RB8.6.B.2",
              "text": "A first-digit change is a port, not a bump: Pedro 2 → 3 renamed nearly everything and replaced the tuners. Read the release notes first."
            },
            {
              "code": "RB8.6.B.3",
              "text": "A bump can break something no compiler sees. Panels fullpanels 1.0.13 pulled in field 1.0.7, which shipped without its web files, and the dashboard's field view silently vanished. The check needs no robot: field 1.0.6's .aar holds six web-asset entries and 1.0.7's holds none (counted 2026-09-19)."
            },
            {
              "code": "RB8.6.B.4",
              "text": "Anything surprising goes in the issue log (RB8.5.B)."
            }
          ]
        },
        {
          "code": "RB8.6.C",
          "text": "Keep the files an upstream owns byte-for-byte, prove it with the structure check, and record any deliberate deviation with its reason.",
          "skill": "RB4.B",
          "evidence": "A",
          "aligns": [],
          "knowledge": [
            {
              "code": "RB8.6.C.1",
              "text": "A short list of files belongs to the upstream, and every one stays byte-for-byte theirs, so next season's merge stays silent."
            },
            {
              "code": "RB8.6.C.2",
              "text": "Tools edit them without asking. Android Studio added a plugin block to settings.gradle during a Gradle sync, and the structure check caught it."
            },
            {
              "code": "RB8.6.C.3",
              "text": "A deliberate deviation is recorded with its reason where the checker reads it. Artemis's compileSdk 34 in two files is the one exception, because FIRST ships 30 and it doesn't build with the team's dependencies. A known deviation is shown as a note, not an error, because \"a warning that never goes away is a warning everyone ignores.\""
            },
            {
              "code": "RB8.6.C.4",
              "text": "The check is judged against the right yardstick, the merge base with the real upstream, and it says so when it can't find one (RB8.5.D)."
            }
          ]
        }
      ]
    }
  ]
}