Skip to content

feat(skill): StateStore + scraper rename + doc date sweep - #12

Merged
alirezarezvani merged 1 commit into
devfrom
claude/polish-foundation
May 19, 2026
Merged

alirezarezvani merged 1 commit into
devfrom
claude/polish-foundation

Conversation

@alirezarezvani

Copy link
Copy Markdown
Owner

Summary

Three small polish items deferred from PR #10, bundled into one PR per your preference.

1. aso_skill.StateStore — minimal per-app state

New module aso_skill/state.py persists per-app state to
outputs/<app>/.state/current.json. Atomic write (tempfile + os.replace)
so a crash during save cannot corrupt the file. Schema-versioned
(schema_version: 1); a future-version file raises rather than silently
corrupting.

Slots: apple_metadata, google_metadata, keyword_set,
last_aso_score, competitors. API: load(), save(), update(**slots),
exists, path. Unknown top-level keys are preserved on load so a future
schema addition doesn't lose data written by newer clients.

This unlocks the ASO Watcher and resume-on-failure work in
follow-up PRs without committing to a larger design surface in this one.
10 new tests in tests/test_state.py.

2. scraper.py → aso_skill/webfetch_prompts.py

The module builds {url, prompt} dicts the agent feeds into its
WebFetch tool — it does no HTTP itself. The name was misleading. New
canonical location is aso_skill/webfetch_prompts.py;
app-store-optimization/lib/scraper.py remains as a backward-compat
symlink. Two doc references updated (ARCHITECTURE.md, data_sources.md).

3. Documentation date sweep

Replaced the 11 hardcoded November 7, 2025 / Date: / Last Updated:
footers across README, INSTALL, USAGE, ARCHITECTURE, the implementation
plan, branch-protection-setup.md, the strategist's "Ongoing Tasks"
template, and CHANGELOG with 2026-05-19. Bumped affected Version:
labels from 1.0 → 1.1.

Intentionally left as-is (historical / immutable):

  • CHANGELOG [1.0.0] - 2025-11-07 version tags
  • Release Date: in the project README
  • Actual Completion: November 7, 2025 in the implementation plan
  • Fetched: November 7, 2025, 10:30 AM PST inside a sample API response
    fixture in data_sources.md

Test plan

  • python3 -m pytest tests/ -q → 60 passed in 2.79s (50 prior + 10 new state tests)
  • ruff check aso_skill scripts tests → All checks passed
  • ruff format --check aso_skill scripts tests → 28 files already formatted
  • readlink -f app-store-optimization/lib/scraper.py → aso_skill/webfetch_prompts.py
  • grep -rn "November 7, 2025" .claude/ documentation/ README.md CHANGELOG.md → only the 6 historical references listed above remain
  • python3 -c "from aso_skill import StateStore; s = StateStore('Demo'); print(s.path)" → outputs/Demo/.state/current.json

https://claude.ai/code/session_01CbA3Mp4rD4vW9Yjd2aPEXw


Generated by Claude Code

Three small polish items in one PR, all deferred from PR #10:

1. aso_skill.StateStore -- minimal per-app state store under
   outputs/<app>/.state/current.json. Atomic write via tempfile +
   os.replace; schema_version: 1; raises on future-version files.
   Unlocks the ASO Watcher and resume-on-failure work without
   committing to a larger design surface.

2. Rename scraper.py -> webfetch_prompts.py. The module builds
   {url, prompt} dicts that the agent feeds into its WebFetch tool;
   it does no HTTP. The old path is preserved as a backward-compat
   symlink so external references keep working.

3. Update the 11 hardcoded November 7, 2025 footers across docs to
   2026-05-19. Historical dates (release tags, "Actual Completion: ..."
   markers, fixture timestamps in sample API responses) are intentionally
   left as-is.

10 new tests in tests/test_state.py. Full suite: 60 passed in 2.79s.

https://claude.ai/code/session_01CbA3Mp4rD4vW9Yjd2aPEXw
@github-actions

Copy link
Copy Markdown

Large PR alert: this PR has 1257 changes. Consider splitting it into smaller PRs (<200 changes) for faster review and fewer conflicts.

@github-actions github-actions Bot added the size: XL Extra large PR (>=1000 changes) label May 19, 2026
@alirezarezvani
alirezarezvani marked this pull request as ready for review May 19, 2026 01:50
@alirezarezvani
alirezarezvani merged commit ac2420f into dev May 19, 2026
11 checks passed
@github-actions

Copy link
Copy Markdown

Branch claude/polish-foundation was automatically deleted after merge.

@github-actions
github-actions Bot deleted the claude/polish-foundation branch May 19, 2026 01:51
@claude

claude Bot commented May 19, 2026

Copy link
Copy Markdown

Code Review — PR #12: StateStore + scraper rename + doc date sweep

Overall: Solid, well-scoped PR. The atomic-write design is correct, tests are comprehensive, and the rename accurately reflects what the module does. Three issues need attention before merge — one security, two correctness.


Bugs / Must-Fix

1. Path traversal in StateStore app_name (security)

state.py lines 737–742 — app_name is used directly in path construction with no sanitization:

self.base_dir / self.app_name / ".state"

StateStore("../../etc/passwd") or StateStore("/absolute") writes outside outputs/. Add a guard in __init__:

app_name = str(app_name).strip()
if not app_name or "/" in app_name or "\\" in app_name or ".." in app_name:
    raise ValueError("app_name must be a plain name with no path separators")
self.app_name = app_name

The existing empty-string check (test_empty_app_name_rejected) should be extended with a test_path_traversal_rejected case.


2. Stale import path inside webfetch_prompts.py docstring

aso_skill/webfetch_prompts.py line 1016 — the usage example inside ScraperGuide.get_usage_instructions() still references the old location:

from app_store_optimization.lib.scraper import WebFetchPrompts

Agents reading this guide will use the wrong import. Should be:

from aso_skill.webfetch_prompts import WebFetchPrompts

Same stale path also appears in data_sources.md — that one was correctly updated (line 159 in diff), but the string inside the Python file was missed.


3. Symlink breaks when standalone skill is distributed

app-store-optimization/lib/scraper.py is a relative symlink pointing to ../../aso_skill/webfetch_prompts.py. This only resolves correctly when both directories are siblings under the project root. Per CLAUDE.md, the standalone skill at app-store-optimization/ is meant to be copied to ~/.claude/skills/ — after that copy, the symlink is dangling since aso_skill/ won't exist there.

Options:

  • Make the symlink target absolute (fragile, machine-specific).
  • Keep scraper.py as a real file that re-exports: from aso_skill.webfetch_prompts import * — but that introduces a coupling assumption.
  • Copy webfetch_prompts.py into app-store-optimization/lib/ during distribution, documented in the sync step in CLAUDE.md.
  • Document the limitation explicitly: the symlink is only valid in-repo, the standalone skill should bundle a copy.

Minor / Non-blocking

save() silently coerces non-serializable types (default=str): unexpected objects (e.g., a datetime) would be saved as strings and loaded back as strings, silently corrupting typed data. Removing default=str and letting json.dump raise would surface bugs earlier. Only suppress it if you intentionally support arbitrary types.

exists() vs property pattern: path and state_dir are @property, but exists() is a method. Reasonable since it does I/O, but the API is slightly inconsistent. Not worth changing now.

ongoing-tasks.md template (.claude/agents/aso/aso-strategist.md line 83): Last Updated: 2026-05-19 is hardcoded inside the Markdown template agents emit. Files generated months from now will have a stale date. Consider using a placeholder the agent fills in at generation time (e.g. {{date}}).

[1.1.0] - Unreleased + [1.1.1] - Unreleased in CHANGELOG: two consecutive unreleased entries. Keep a Changelog convention is one [Unreleased] block at the top. Fine to keep if 1.1.0 is a distinct prior PR's entry, but worth a comment in the CHANGELOG header clarifying the intent.


What's Good

  • Atomic write (tempfile.mkstemp + os.replace) is the correct POSIX pattern; crash-safe.
  • Schema versioning with unknown-key preservation is well-designed for forward compat.
  • 10 tests cover the important cases: round-trip, stamping, unknown slots, future schema version, no leftover temp files.
  • Zero external dependencies — stdlib only.
  • aeo-skill → aso-skill typo fix in ARCHITECTURE.md is a good catch.
  • CHANGELOG follows Keep a Changelog format correctly.
  • ruff clean per test plan.

Verdict: Request changes on issues #1 (security) and #2 (stale import in docstring). Issue #3 needs a decision, but the right fix is cheap. Everything else can be addressed as follow-ups.

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

Labels

size: XL Extra large PR (>=1000 changes)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants