Skip to content

Add AI agent workflows and guardrails for code development - #2822

Open
MarkYav wants to merge 3 commits into
google:mainfrom
MarkYav:agent-guidelines
Open

Add AI agent workflows and guardrails for code development#2822
MarkYav wants to merge 3 commits into
google:mainfrom
MarkYav:agent-guidelines

Conversation

@MarkYav

@MarkYav MarkYav commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator

WHAT

This PR introduces rules and specialized sub-agents to guide AI coding agents.

WHY

By formalizing these operational guardrails, we ensure that coding agents adhere to the team's and project's coding preferences, and maintain uniform documentation quality without relying on manual intervention. This is especially critical for open-source projects.

As the first step, these changes are specific to :remotecompose:lottie module, but can be extended to the whole Horologist repository.

HOW

  • Routing & catalog (AGENTS.md): Points agents to canonical Lottie specifications and acts as the entry point for all workflows.
  • Strict TDD enforcement (dev-test.md): Adds an isolated dev-test subagent tasked with generating black-box functional tests prior to any feature implementation.
  • Execution & Git discipline: Institutionalizes atomic commits and splits functional/non-functional work via commit_rules.md.
  • Testing & review criteria: Sets explicit expectations for code reviews, code documentation, and module-specific Gradle execution orders natively comprehensible to AI assistants.

Checklist 📋

  • Add explicit visibility modifier and explicit return types for public declarations
  • Run spotless check
  • Run tests
  • Update metalava's signature text files

…ment.

### Motivation
This introduces strict machine-readable rules and specialized subagents to guide AI-assisted tasks. By formalizing these operational guardrails, we ensure that coding agents adhere to the team's and project's coding preferences, and maintain uniform documentation quality without relying on manual intervention.

### Technical Context
- Routing & Catalog (`AGENTS.md`): Points agents to canonical Lottie specifications and acts as the entry point for all workflows.
- Strict TDD Enforcement (`dev-test.md`): Adds an isolated `dev-test` subagent tasked with generating black-box functional tests prior to any feature implementation.
- Execution & Git Discipline: Institutionalizes atomic commits and splits functional/non-functional work via `commit_rules.md`.
- Testing & Review Criteria: Sets explicit expectations for code reviews, code documentation, and module-specific Gradle execution orders natively comprehensible to AI assistants.
@github-actions

This comment was marked as off-topic.

@MarkYav

MarkYav commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator Author

For the given class

internal object BaseVectorPropertySerializer :
  JsonContentPolymorphicSerializer<BaseVectorProperty>(BaseVectorProperty::class) {
  override fun selectDeserializer(
    element: JsonElement
  ): DeserializationStrategy<BaseVectorProperty> {
    val obj = element as? JsonObject ?: throw SerializationException("Expected JSON object")
    val animated = obj["a"]?.jsonPrimitive?.intOrNull
    return when (animated) {
      1 -> AnimatedVectorProperty.serializer()
      0 -> StaticVectorProperty.serializer()
      null -> throw SerializationException("Vector property missing required 'a' field per Lottie schema")
      else -> throw SerializationException("Field 'a' must be 0 or 1, but was $animated")
    }
  }
}

Generated comment before:

/** Polymorphic serializer for [BaseVectorProperty] based on "a" field. */

And after:

/**
 * Polymorphic serializer for [BaseVectorProperty] discriminating between static and animated
 * variants based on the Lottie schema `"a"` field ([Integer
 * Boolean](https://lottie.github.io/lottie-spec/dev/specs/values/#int-boolean)).
 *
 * Contract:
 * - Preconditions: [element] must be a [JsonObject].
 * - Postconditions:
 *     - Selects [AnimatedVectorProperty.serializer] when `"a"` is integer `1`.
 *     - Selects [StaticVectorProperty.serializer] when `"a"` is integer `0`.
 * - Exceptions:
 *     - Throws [SerializationException] if [element] is not a [JsonObject].
 *     - Throws [SerializationException] if `"a"` is missing.
 *     - Throws [SerializationException] if `"a"` is neither `0` nor `1`.
 */

@MarkYav

MarkYav commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator Author

Prompt (not tuned): Explore the Lottie specification for the vector property, and generate corresponding tests to cover the specifications.

Model: Gemini 3.8 Flash

Generated report (click to open):

Lottie Vector Property: Specification Analysis & Test Plan

Canonical Specification Reference


1. Domain Structuring & Equivalence Partitioning

Domain A: Format AST & Deserialization (VEC-PARSE)

Parameter Equivalence Class Selected Boundary / Concrete Values
a (Animated Flag) EC-A1 (Valid Default): Omitted (implicit 0)
EC-A2 (Valid Static): 0
EC-A3 (Valid Animated): 1
EC-A4 (Invalid): 2, -1, "0", {}
null (omitted), 0, 1, 2, "true"
sid (Slot ID) EC-SID1 (Valid Absent): Omitted / null
EC-SID2 (Valid Non-empty): String identifier
EC-SID3 (Valid Empty): Empty string ""
null, "vector.scale", ""
k (Static Vector) EC-SK1 (Nominal 2D): Standard 2D vector
EC-SK2 (Boundary 1D): Single component scalar vector
EC-SK3 (Boundary 3D): Standard 3D vector
EC-SK4 (Boundary 4D): 4-component vector
EC-SK5 (Boundary 0D): Empty array []
EC-SK6 (Numeric Extremes): Negative, zero, integer-literal, decimals
EC-SK7 (Invalid Types): Primitive number, object, string, non-numeric elements
[100.0, 200.0], [42.0], [1.0, 2.0, 3.0], [1.0, 0.5, 0.2, 1.0], [], [0.0, -15.5, 100.0], 10.5, ["invalid"]
k (Animated Keyframes) EC-AK1 (Nominal Multi-KF): $N \ge 2$
EC-AK2 (Boundary 1-KF): $N = 1$
EC-AK3 (Boundary 0-KF): $N = 0$ ([])
EC-AK4 (Invalid): Missing field, array of non-objects
2 keyframes, 3 keyframes, 1 keyframe, [], {}
t (Frame Time) EC-KT1 (Nominal): Positive float
EC-KT2 (Boundary Zero): 0.0
EC-KT3 (Default Absent): Omitted (defaults to 0.0)
EC-KT4 (Fractional): Sub-frame time
EC-KT5 (Negative): Timeline before zero
10.0, 0.0, omitted, 14.5, -5.0
s (Start Value) EC-KS1 (Nominal): Dimension-matching float array
EC-KS2 (Boundary Empty): []
EC-KS3 (Invalid): Missing s, non-array
[1.0, 2.0], [], null/missing, "abc"
h (Hold Flag) EC-KH1 (Default Absent): Omitted (defaults to 0 / false)
EC-KH2 (Int Zero): 0 (false)
EC-KH3 (Int One): 1 (true)
EC-KH4 (Bool False): false
EC-KH5 (Bool True): true
EC-KH6 (Invalid): Non-boolean/int primitive
omitted, 0, 1, false, true, "invalid"
i & o (Easing Tangents) EC-KE1 (Default Absent): Omitted (linear)
EC-KE2 (Nominal Scalar): {"x": 0.4, "y": 0.8}
EC-KE3 (Array Format): {"x": [0.4], "y": [0.8]}
EC-KE4 (Extremes): Clamped limits 0.0 and 1.0
EC-KE5 (Invalid): Non-numeric, missing coords
omitted, {"x": 0.4, "y": 0.8}, {"x": [0.4], "y": [0.8]}, {"x": 0.0, "y": 1.0}, {"x": "bad"}

Domain B: Animation & Evaluation Semantics (VEC-ANIM)

Dimension Equivalence Class Selected Boundary / Concrete Values
Vector Dimensionality ($D$) EC-VD1: 1D ([X])
EC-VD2: 2D ([X, Y])
EC-VD3: 3D ([X, Y, Z])
EC-VD4: 4D ([R, G, B, A])
EC-VD5: 0D ([])
1D: [50f], 2D: [100f, 200f], 3D: [1f, 2f, 3f], 4D: [1f, 0.5f, 0.2f, 1f], 0D: floatArrayOf()
Keyframe Topology EC-TP1: Single Keyframe ($N=1$)
EC-TP2: Two Keyframes starting at $t=0$
EC-TP3: Delayed Start ($t_0 &gt; 0$)
EC-TP4: Multi-segment Sequence ($N \ge 3$)
EC-TP5: Coincident Keyframes ($t_i = t_{i+1}$)
$N=1$ at $t=0$; $N=2$ at $t=[0, 10]$; $N=2$ at $t=[10, 20]$; $N=3$ at $t=[0, 10, 20]$; $N=2$ at $t=[10, 10]$
Evaluation Temporal Point ($t$) EC-TE1: Pre-animation underflow ($t &lt; t_0$)
EC-TE2: Exact start boundary ($t = t_0$)
EC-TE3: Intermediate progress ($t_0 &lt; t &lt; t_{end}$)
EC-TE4: Exact end boundary ($t = t_{end}$)
EC-TE5: Post-animation overflow ($t &gt; t_{last}$)
$t = -5.0$, $t = 0.0$, $t = 5.0$, $t = 10.0$, $t = 15.0$
Interpolation Mode EC-IM1: Linear default (tangents absent)
EC-IM2: Cubic Bézier easing
EC-IM3: Hold step ($h = 1$ / $true$)
EC-IM4: Mixed chaining (e.g. Seg 1 Hold, Seg 2 Bézier)
Linear (0,0)->(1,1); S-Curve (0.4, 0.0)->(0.2, 1.0); Hold $h=1$; Chained [Hold -> Bezier]

2. Semantic Constraints & Pruning Rules

  1. Static Exclusivity: When a = 0 (or omitted), keyframes do not exist. Evaluation is invariant across all query frames $t$.
  2. Single Keyframe Invariance: When $N = 1$, interpolation modes (h, i, o) are inactive because there is no target frame. Value is strictly $kf_0.s$ for all $t$.
  3. Hold Precedence: When $h = 1$ (or true), Bézier control tangents $i$ and $o$ are suppressed; the value is held constant at $kf_i.s$ across $[t_i, t_{i+1})$.
  4. Dimensional Consistency: In valid animations, all keyframes share identical vector dimensionality ($length(kf_0.s) == length(kf_i.s)$).
  5. Coincident Segment Bound: For zero-duration segments where $t_i = t_{i+1}$, evaluation at $t \ge t_{i+1}$ takes $kf_{i+1}.s$ without division by zero.

3. Pairwise Combinatorial Test Grid

Table 1: Deserialization / AST Tests (VEC-PARSE)

Test ID Property Type Input Fields & Boundary Setup Verification Assertions
VEC-PARSE-001 Static {"a": 0, "k": [10.0, 20.0]} StaticVectorProperty, animated == false, value == [10f, 20f], slotId == null
VEC-PARSE-002 Static {"sid": "vec.scale", "k": [1.0, 2.0, 3.0]} (omitted a) StaticVectorProperty, animated == false, slotId == "vec.scale", 3D array
VEC-PARSE-003 Static {"a": 0, "k": [42.5]} StaticVectorProperty, 1D float array
VEC-PARSE-004 Static {"a": 0, "k": []} StaticVectorProperty, empty array FloatArray(0)
VEC-PARSE-005 Static {"a": 0, "k": [0, -15.5, 1000]} Correctly parses zero, negative float, and integer literal into floats
VEC-PARSE-006 Static {"a": 0, "k": 10.5} Throws SerializationException (non-array scalar)
VEC-PARSE-007 Static {"a": 0, "k": ["invalid"]} Throws SerializationException (non-numeric element)
VEC-PARSE-008 Animated {"a": 1, "k": [{"t": 0, "s": [1.0, 2.0]}, {"t": 10, "s": [3.0, 4.0]}]} AnimatedVectorProperty, animated == true, 2 keyframes
VEC-PARSE-009 Animated {"a": 1, "k": [{"t": 0, "s": [10.0]}]} Single keyframe ($N=1$)
VEC-PARSE-010 Animated {"a": 1, "k": []} Empty keyframes list
VEC-PARSE-011 Animated Keyframe with "h": 1 and "h": 0 (int-boolean) Correctly parses hold == true and hold == false
VEC-PARSE-012 Animated Keyframe with "h": true and "h": false (boolean) Correctly parses hold == true and hold == false
VEC-PARSE-013 Animated Keyframe with omitted "h" Defaults to hold == false
VEC-PARSE-014 Animated Keyframe with scalar easing tangents: "i": {"x": 0.2, "y": 0.8} ScalarKeyframeEasing(0.2f, 0.8f)
VEC-PARSE-015 Animated Keyframe with array easing tangents: "i": {"x": [0.2], "y": [0.8]} ScalarKeyframeEasing(0.2f, 0.8f)
VEC-PARSE-016 Animated Keyframe with omitted tangents Tangents deserialize to null
VEC-PARSE-017 Animated Keyframe with fractional frame time "t": 12.5 frame == 12.5f
VEC-PARSE-018 Animated Keyframe with omitted frame time "t" Defaults to frame == 0f
VEC-PARSE-019 Animated Keyframe with negative frame time "t": -5.0 frame == -5f
VEC-PARSE-020 Animated Keyframe missing required "s" Throws SerializationException
VEC-PARSE-021 Polymorphic Round-trip serialization of StaticVectorProperty Re-deserialized object equals original
VEC-PARSE-022 Polymorphic Round-trip serialization of AnimatedVectorProperty Re-deserialized object equals original

Table 2: Animation Evaluation Tests (VEC-ANIM)

Test ID Dimensionality Topology & Easing Query Point ($t$) Expected Outcome
VEC-ANIM-001 3D Static [1f, 2f, 3f] $t = -10, 0, 50, 1000$ Returns [1f, 2f, 3f] at all frame times
VEC-ANIM-002 2D Single keyframe at $t=5$: [10f, 20f] $t = 0, 5, 15$ Returns [10f, 20f] at all frame times
VEC-ANIM-003 3D Two keyframes $t=[0, 10]$, linear: [0, 10, 20] -> [10, 20, 30] Exact start: $t = 0$ Returns [0f, 10f, 20f]
VEC-ANIM-004 3D Two keyframes $t=[0, 10]$, linear: [0, 10, 20] -> [10, 20, 30] Midpoint: $t = 5$ Returns arithmetic mean [5f, 15f, 25f]
VEC-ANIM-005 3D Two keyframes $t=[0, 10]$, linear: [0, 10, 20] -> [10, 20, 30] Exact end: $t = 10$ Returns [10f, 20f, 30f]
VEC-ANIM-006 3D Two keyframes $t=[0, 10]$, linear: [0, 10, 20] -> [10, 20, 30] Overflow: $t = 15$ Clamps and holds [10f, 20f, 30f]
VEC-ANIM-007 2D Delayed start $t=[10, 20]$: [100f, 200f] -> [300f, 400f] Underflow: $t = 0, 5$ Holds initial keyframe [100f, 200f]
VEC-ANIM-008 2D Hold keyframe ($h=1$, int) $t=[0, 10]$: [10f, 20f] -> [30f, 40f] $t = 5$ (mid), $t = 10$ (end) At $t=5$: holds [10f, 20f]. At $t=10$: steps to [30f, 40f]
VEC-ANIM-009 2D Hold keyframe ($h=true$, bool) $t=[0, 10]$: [10f, 20f] -> [30f, 40f] $t = 9.9$, $t = 10.0$ At $t=9.9$: holds [10f, 20f]. At $t=10$: steps to [30f, 40f]
VEC-ANIM-010 2D Cubic Bézier easing ($o=(0.4, 0.0), i=(0.2, 1.0)$) $t=[0, 10]$: [0f, 0f] -> [100f, 100f] $t = 5$ (mid) Non-linear S-curve evaluated value $\ne 50f$ matching Bézier lookup
VEC-ANIM-011 2D 3-keyframe sequence $t=[0, 10, 20]$: [0, 0] -> [10, 20] -> [30, 40] $t = 5$ (seg 1), $t = 15$ (seg 2) At $t=5$: [5f, 10f]. At $t=15$: [20f, 30f]
VEC-ANIM-012 2D Mixed: Seg 1 Hold ($h=1$), Seg 2 Linear ($h=0$) across $t=[0, 10, 20]$ $t = 5, 10, 15$ At $t=5$: hold kf_0. At $t=10$: kf_1. At $t=15$: lerp mid (kf_1 + kf_2)/2
VEC-ANIM-013 2D Mixed: Seg 1 Bézier, Seg 2 Hold ($h=1$) across $t=[0, 10, 20]$ $t = 5, 15$ At $t=5$: bézier lerp. At $t=15$: hold kf_1
VEC-ANIM-014 1D 1D vector interpolation: [100f] -> [200f] over $t=[0, 10]$ $t = 5$ Returns 1D list [150f]
VEC-ANIM-015 2D Inverse directions: $X$ increasing 0->100, $Y$ decreasing 200->50 $t = 5$ $X = 50f$, $Y = 125f$
VEC-ANIM-016 4D 4-vector interpolation: [1, 0, 0.5, 1] -> [0, 1, 0.5, 0] $t = 5$ [0.5f, 0.5f, 0.5f, 0.5f]
VEC-ANIM-017 2D Coincident keyframes $t=[10, 10]$: [10f, 20f] -> [30f, 40f] $t = 10$ Evaluates cleanly to [30f, 40f] without NaN / div-by-zero
VEC-ANIM-018 2D Negative query time $t = -5$ for $t=[0, 10]$ $t = -5$ Holds start value kf_0.s
VEC-ANIM-019 2D Negative timeline keyframes $t=[-10, 10]$: [-50f, 0f] -> [50f, 100f] $t = 0$ (midpoint) Evaluates arithmetic midpoint [0f, 50f] crossing $t=0$ smoothly
VEC-ANIM-020 2D Extreme magnitude floats ($10^6$ and $-10^6$) $t = 5$ on $t=[0, 10]$ Precision preserved without float overflow

Example of a generated test (bug caught):

  /**
   * [VEC-PARSE-011] Deserializes keyframe hold flag represented as integer-boolean `h = 1` and `h =
   * 0`.
   *
   * Root cause: Specification requires `h` in
   * [Lottie Integer Boolean](https://lottie.github.io/lottie-spec/1.0.1/specs/values/#int-boolean)
   * to accept integer boolean `0` or `1`. VectorPropertyKeyframe currently relies on the default
   * boolean serializer which rejects integers with JsonDecodingException ("Expected boolean, but
   * had 1"). A custom serializer supporting both boolean and int-boolean (similar to
   * ScalarPropertyKeyframeSerializer) is required in production code.
   *
   * Specification:
   * [Lottie Integer Boolean](https://lottie.github.io/lottie-spec/1.0.1/specs/values/#int-boolean)
   */
  @Ignore(
    "BUG: VEC-PARSE-011: VectorPropertyKeyframe does not deserialize int-boolean (0/1) for hold property 'h'"
  )
  @Test
  fun deserializesVectorKeyframe_whenHoldFlagIsIntegerBoolean() {
    val jsonHoldTrue = """{"t": 0, "h": 1, "s": [1.0, 2.0]}"""
    val keyframeTrue =
      LottieDecoder.json.decodeFromString(VectorPropertyKeyframe.serializer(), jsonHoldTrue)
    assertThat(keyframeTrue.hold).isTrue()

    val jsonHoldFalse = """{"t": 0, "h": 0, "s": [1.0, 2.0]}"""
    val keyframeFalse =
      LottieDecoder.json.decodeFromString(VectorPropertyKeyframe.serializer(), jsonHoldFalse)
    assertThat(keyframeFalse.hold).isFalse()
  }

@MarkYav

MarkYav commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator Author

During the tests construction, the agent generates a report (example of the report is attached to #2822 (comment)) and the question is if we should include it in the codebase or not.

There is an option to

  1. include the generated report in a docs/ folder of the module, or
  2. use this document only for the test generation, but discard after the usage religning on the description of the tests in comments and test logic.

I personally use Spec-Driven Development (SDD) a lot, and I have a separate (nested) folder for the artifacts. This aligns very well with the artifacts, generated by this agent. But they don't go into the production code. After certain threshold, I start tracking them using git as a separate repository.

@yschimke yschimke left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should start off small, and make maybe something 1/5 the size part of the general horologist guidance. For Lottie maybe focus on the spec and how to test against the lottie spec.

comparing lottie guidance to horologist then we should either

  • make it general horologist guidance
  • question why lottie differs

Comment thread remotecompose/lottie/.agents/rules/code_documentation_rules.md Outdated
@@ -0,0 +1,85 @@
---
name: dev-test

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels like there is a whitepaper behind it, but I'm not convinced being this prescriptive will help in two ways

  1. friction depending on the agents used, overall introduces a lot of process for something simple.
  2. if this is best practice as of September 2026, how do we know it will continue.


---

## VIOLATION DETECTED (SELF-CORRECTION TRAP)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels like something a harness should do, not guidance in every agent file.

subagent: true
---

# Dev-Test: Stringent Functional Testing Generator

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure all horologist tests need to be stringent like this. White box testing can have a place, and sometimes code is exploratory, and not worth extensive tests initially.

# RemoteCompose Lottie Guidelines

<lottie_specifications>
## Canonical Lottie Specifications

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, this is useful.

description: "Guidelines and template for drafting Pull Request descriptions and checklists."
---

# Pull Request Guidelines

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could just be a link to the actual template

@MarkYav MarkYav Sep 7, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea! Should we move this rule to the root, so it affects all the agents that are working on Horologist, not only on the Lottie module?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, let's move it to the root.

Comment thread remotecompose/lottie/.agents/rules/commit_rules.md Outdated

### A. The Call (Preconditions & Input State)
Document all constraints required for the caller to invoke the method safely:
- **Nullability:** Explicitly declare whether arguments can be null (`@NonNull`, `@Nullable`).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems to be java related, not a think in Kotlin?

- **Value Bounds:** Specify ranges (e.g., `offset >= 0`, `len >= 0`).
- **Relational Constraints:** Specify cross-parameter relationships (e.g., `offset + len <= cbuf.length`).

### B. The Results (Postconditions & Output State)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels heavyweight and prescriptive and may be confused with Compose code.

3. Error Hotspots: `null`, `[]`, empty strings, missing fields, 32-bit/64-bit limits, boundary±1 values, floating-point precision/epsilon.
4. Domain Anomalies: Degenerate geometry (e.g., $<2$ points in Bézier, out-of-range colors, invalid easing tangents, inverted keyframe time).

## STATE 3: COMBINATORIAL SELECTION & PAIRWISE GRID

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this matches what we do in Horologist, for any UI components we tend to focus on screenshot tests of key states, and avoid explosions of values tested.

### Impact Summary
Simplifies the AI coding assistant instructions for `:remotecompose:lottie` by eliminating
overly prescriptive rule files. This makes the instructions more generic and better aligns pull with existing Horologist repository standards.

### External References
- Horologist Pull Request Template: https://github.com/google/horologist/blob/main/.
  github/pull_request_template.md

TAG=agy
CONV=acc9e3d7-2a7e-4c05-8437-7e64d07b6298
@MarkYav

MarkYav commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator Author

Reduced the size of the instructions to target Lottie spec, commits and PRs.

- Information that is obvious from reading the code.
- Code maintenance instructions (place these in code comments instead).
- Short-term discussions.
- Preview URLs and build artifacts.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are preview URLs banned?


## 6. Scope and Isolation
- **DO NOT** mix functional and non-functional changes in the same commit. If you are asked to add a feature, do not simultaneously reformat surrounding code, reorganize imports, or refactor unrelated methods in the same commit.
- Keep commits atomic (narrowly scoped): Split into **Commit A (Refactor/Cleanup)** and **Commit B (Feature Logic)** when preparing code for a new feature.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this about commits? or PRs?

- Preview URLs and build artifacts.

## 6. Scope and Isolation
- **DO NOT** mix functional and non-functional changes in the same commit. If you are asked to add a feature, do not simultaneously reformat surrounding code, reorganize imports, or refactor unrelated methods in the same commit.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we weaken DO NOT, to PREFER to separate...?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants