{
  "featureName": "sdk-size-reduction",
  "branchName": "fester/sdk-size-reduction",
  "baseBranch": "main",
  "baseBranchNote": "The repository has both 'main' and 'master' branches. 'main' is intentional — the feature branch fester/sdk-size-reduction targets 'main', which then merges to 'master'. Confirmed intentional.",
  "createdAt": "2026-03-16",
  "specPath": "planning/sdk-size-reduction/spec.md",
  "reviewPath": "planning/sdk-size-reduction/spec-review.md",
  "maxIterations": 12,
  "stories": [
    {
      "id": "story-1",
      "title": "Add ordering constraint comment to initEventSpecModules()",
      "description": "The initEventSpecModules() method in src/AvoInspector.ts has a subtle race condition hazard: all instance field assignments (this.eventSpecCache, this.eventSpecFetcher, this._validateEvent) must occur inside the try block, before the finally block sets this._eventSpecReady = undefined. Concurrent callers that await this._eventSpecReady resume after the promise resolves and must find all fields already populated. If assignments were moved after or into the finally block, concurrent callers would see undefined fields immediately after awaiting. This constraint is documented only in the spec edge cases section and must be documented in the code itself (Remaining Work #13). Add a multi-line comment above the try block inside initEventSpecModules() in src/AvoInspector.ts (around line 147) explaining the ordering requirement. The comment must mention _eventSpecReady, finally, and the race condition risk.",
      "approach": "general-purpose",
      "acceptanceCriteria": [
        "src/AvoInspector.ts initEventSpecModules() contains a comment above the try block (around line 147) documenting the ordering constraint",
        "The comment explicitly mentions _eventSpecReady, finally, and the race condition risk for concurrent callers",
        "The comment explains that assignments must precede the finally block so concurrent await _eventSpecReady callers find all fields populated",
        "yarn build exits 0",
        "yarn test --roots='<rootDir>/src' passes",
        "BROWSER=1 yarn test --roots='<rootDir>/src' passes"
      ],
      "priority": "medium",
      "dependsOn": [],
      "estimatedFiles": 1,
      "qualityChecks": [
        {
          "command": "yarn build",
          "required": true,
          "description": "TypeScript compilation must succeed after comment addition"
        },
        {
          "command": "yarn test --roots='<rootDir>/src'",
          "required": true,
          "description": "All tests must pass (comment-only change)"
        },
        {
          "command": "BROWSER=1 yarn test --roots='<rootDir>/src'",
          "required": true,
          "description": "Browser test suite must pass"
        }
      ],
      "status": "pending",
      "testPattern": "No new tests. Comment-only change to src/AvoInspector.ts; existing suite must pass unchanged."
    },
    {
      "id": "story-2",
      "title": "Add dynamic import failure test and success-path assertion for initEventSpecModules()",
      "description": "The initEventSpecModules() method in src/AvoInspector.ts has a try/catch block that handles dynamic import failures. The graceful degradation path — where a failed import causes trackSchemaFromEvent to fall through to the batched flow without surfacing an error to the caller — has no test coverage (Remaining Work #12). Additionally, the spec acceptance criterion requires asserting that in a dev/staging environment with a valid streamId, initEventSpecModules() successfully loads all three eventSpec modules and ensureEventSpecReady() returns a non-null object with cache, fetcher, validate, and streamId fields.\n\nIMPORTANT — Test isolation requirement: Do NOT add these tests to the existing src/__tests__/AvoInspectorEventSpec_test.ts file. That file imports AvoInspector at the top level (line 1: import { AvoInspector } from '../AvoInspector'), which is resolved by Jest before any jest.doMock() call inside a test body. To intercept the dynamic require() calls compiled from import() expressions (TypeScript compiles import() to Promise.resolve().then(() => require(...))), the test must: (1) call jest.resetModules(), (2) call jest.doMock() for each eventSpec module, and (3) re-require AvoInspector inside the test body using require('../AvoInspector'). This pattern requires AvoInspector to be required inside the test, not imported at the top of the file.\n\nCreate a new, isolated test file: src/__tests__/AvoInspectorDynamicImportFailure_test.ts\n\nThis file should NOT have a top-level import of AvoInspector. Instead, require AvoInspector inside each test body after jest.resetModules() + jest.doMock().\n\nTest 1 — Failure path (graceful degradation):\n- Use jest.resetModules() then jest.doMock('../eventSpec/AvoEventSpecCache', () => { throw new Error('import failed'); }) (and similarly for AvoEventSpecFetcher and EventValidator)\n- Use const { AvoInspector } = require('../AvoInspector') inside the test body after mocking\n- Construct a dev-env AvoInspector\n- Wait for the initialization failure to settle (await inspector._eventSpecReady or a short setTimeout)\n- Assert (inspector as any).eventSpecCache is undefined\n- Assert trackSchemaFromEvent returns a defined array with length > 0 (graceful degradation — event still tracked via batched flow)\n\nTest 2 — Success path:\n- Call jest.resetModules() without mocking any module (use real modules)\n- Use const { AvoInspector } = require('../AvoInspector') inside the test body\n- Construct a dev-env AvoInspector with a valid streamId\n- Await (inspector as any)._eventSpecReady\n- Assert ensureEventSpecReady() (called via (inspector as any).ensureEventSpecReady()) returns a non-null object with cache, fetcher, validate, and streamId fields",
      "approach": "test-driven",
      "acceptanceCriteria": [
        "A new test file src/__tests__/AvoInspectorDynamicImportFailure_test.ts is created",
        "The file does NOT import AvoInspector at the top level — AvoInspector is required inside each test body after jest.resetModules()",
        "A failure-path test exists that asserts trackSchemaFromEvent returns a defined array with length > 0 (not just 'resolves') when initEventSpecModules() fails due to a mocked import error",
        "The failure-path test asserts (inspector as any).eventSpecCache is undefined after the failure",
        "A success-path test exists that awaits _eventSpecReady and asserts ensureEventSpecReady() returns a non-null object containing cache, fetcher, validate, and streamId fields",
        "yarn test --roots='<rootDir>/src' passes including both new tests",
        "BROWSER=1 yarn test --roots='<rootDir>/src' passes",
        "yarn build exits 0"
      ],
      "priority": "high",
      "dependsOn": [],
      "estimatedFiles": 1,
      "qualityChecks": [
        {
          "command": "yarn build",
          "required": true,
          "description": "TypeScript compilation must succeed"
        },
        {
          "command": "yarn test --roots='<rootDir>/src'",
          "required": true,
          "description": "All tests including both new tests in AvoInspectorDynamicImportFailure_test.ts must pass"
        },
        {
          "command": "BROWSER=1 yarn test --roots='<rootDir>/src'",
          "required": true,
          "description": "Browser test suite must pass"
        }
      ],
      "status": "pending",
      "testPattern": "New isolated test file: src/__tests__/AvoInspectorDynamicImportFailure_test.ts. No top-level AvoInspector import — use jest.resetModules() + jest.doMock() + require() inside each test body. Two tests: (1) failure path — assert trackSchemaFromEvent returns defined array length > 0 and eventSpecCache is undefined; (2) success path — assert ensureEventSpecReady() returns non-null with {cache, fetcher, validate, streamId}."
    },
    {
      "id": "story-3",
      "title": "Run and verify full prepublishOnly flow",
      "description": "The full publish gate (prepublishOnly) must pass before the feature is merge-ready. The sequence is: yarn build && yarn test:browser && rm -rf examples/ts-avo-inspector-example/node_modules/ && cd examples/ts-avo-inspector-example/ && yarn && yarn test --watchAll=false. This exercises the clean build, browser test suite, and the example project with a fresh install from the local package (using 'avo-inspector': 'file:../..').  The example project (examples/ts-avo-inspector-example/) uses react-scripts test. If any example project test fails due to the ESM dual build (the 'module' and 'exports' fields added to package.json), diagnose and fix in this story. The acceptance criterion '[ ] Example project tests pass' in the spec must be satisfied.\n\nNote: dist-native/ is already gitignored (since commit 350b785 in 2020) and contains zero tracked files — it has no impact on this flow. No dist-native/ cleanup action is required.",
      "approach": "general-purpose",
      "acceptanceCriteria": [
        "yarn build exits 0 (clean build: rm -rf dist && tsc && tsc -p tsconfig.esm.json)",
        "yarn test:browser exits 0 (BROWSER=1 jest)",
        "cd examples/ts-avo-inspector-example && yarn exits 0 (fresh install from local package)",
        "cd examples/ts-avo-inspector-example && yarn test --watchAll=false exits 0",
        "Full prepublishOnly sequence exits 0 end-to-end",
        "No regressions in example project caused by ESM dual build exports fields"
      ],
      "priority": "high",
      "dependsOn": ["story-1", "story-2"],
      "estimatedFiles": 0,
      "estimatedFilesNote": "Verification only — 0 expected changes. Up to 2 files may change if ESM dual build causes example project regressions that must be fixed.",
      "qualityChecks": [
        {
          "command": "yarn build",
          "required": true,
          "description": "Clean build must succeed"
        },
        {
          "command": "yarn prepublishOnly",
          "required": true,
          "description": "Full prepublishOnly sequence must pass end-to-end"
        },
        {
          "command": "cd examples/ts-avo-inspector-example && yarn test --watchAll=false",
          "required": true,
          "description": "Example project tests must pass with fresh local install"
        }
      ],
      "status": "pending",
      "testPattern": "No new unit tests. Run prepublishOnly sequence end-to-end. Fix any example project test failures caused by ESM dual build package.json fields if found."
    }
  ]
}
