abstractskill 0.3.3__tar.gz → 0.3.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. {abstractskill-0.3.3/src/abstractskill.egg-info → abstractskill-0.3.4}/PKG-INFO +6 -2
  2. {abstractskill-0.3.3 → abstractskill-0.3.4}/README.md +5 -1
  3. {abstractskill-0.3.3 → abstractskill-0.3.4}/pyproject.toml +1 -1
  4. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/__init__.py +1 -1
  5. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/catalog.yaml +1 -1
  6. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/abstract-release/SKILL.md +143 -6
  7. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/abstract-release/references/package-inventory.md +12 -3
  8. abstractskill-0.3.4/src/abstractskill/registry/skills/abstract-release/references/readiness-audit.md +54 -0
  9. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/abstract-release/scripts/abstract_release_plan.py +20 -1
  10. abstractskill-0.3.4/src/abstractskill/registry/skills/abstractframework-website/SKILL.md +110 -0
  11. abstractskill-0.3.4/src/abstractskill/registry/skills/abstractframework-website/references/check_site.py +83 -0
  12. abstractskill-0.3.4/src/abstractskill/registry/skills/abstractframework-website/references/preview_server.py +95 -0
  13. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/validations.yaml +39 -5
  14. {abstractskill-0.3.3 → abstractskill-0.3.4/src/abstractskill.egg-info}/PKG-INFO +6 -2
  15. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill.egg-info/SOURCES.txt +4 -0
  16. {abstractskill-0.3.3 → abstractskill-0.3.4}/tests/test_bundled.py +4 -4
  17. {abstractskill-0.3.3 → abstractskill-0.3.4}/tests/test_shelf.py +6 -1
  18. {abstractskill-0.3.3 → abstractskill-0.3.4}/LICENSE +0 -0
  19. {abstractskill-0.3.3 → abstractskill-0.3.4}/setup.cfg +0 -0
  20. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/bundled.py +0 -0
  21. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/catalog.py +0 -0
  22. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/demand.py +0 -0
  23. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/errors.py +0 -0
  24. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/hash.py +0 -0
  25. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/loader.py +0 -0
  26. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/models.py +0 -0
  27. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/parser.py +0 -0
  28. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/policy.py +0 -0
  29. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/prompt.py +0 -0
  30. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/advisories.yaml +0 -0
  31. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/guidance.yaml +0 -0
  32. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/licenses/meshvault-live-editing.LICENSE +0 -0
  33. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/licenses/verification-before-completion.LICENSE +0 -0
  34. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/abstract-release/agents/openai.yaml +0 -0
  35. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/abstract-release/references/package-discovery.md +0 -0
  36. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/abstract-release/references/release-process.md +0 -0
  37. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/abstract-release/scripts/pypi_visibility_gate.py +0 -0
  38. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/abstractframework-gateway/SKILL.md +0 -0
  39. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/adr/SKILL.md +0 -0
  40. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/adr/agents/openai.yaml +0 -0
  41. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/adr/references/adr-template-and-workflow.md +0 -0
  42. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/adr/references/default-adr-catalog.md +0 -0
  43. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/adr/references/reader-first-adr-style.md +0 -0
  44. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/adversarial-iteration/SKILL.md +0 -0
  45. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/agora-collaboration/SKILL.md +0 -0
  46. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/agora-collaboration/references/failure-ledger.md +0 -0
  47. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/agora-collaboration/references/mechanics.md +0 -0
  48. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/architect/SKILL.md +0 -0
  49. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/architect/agents/openai.yaml +0 -0
  50. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/architect/references/reviewer-memory.md +0 -0
  51. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/backlog/SKILL.md +0 -0
  52. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/backlog/agents/openai.yaml +0 -0
  53. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/backlog/references/abstractframework-board.md +0 -0
  54. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/backlog/references/hub-work-join.md +0 -0
  55. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/backlog/references/layout-and-templates.md +0 -0
  56. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/backlog/references/maintenance-checklists.md +0 -0
  57. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/cicd/SKILL.md +0 -0
  58. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/cicd/agents/openai.yaml +0 -0
  59. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/cicd/references/docs-and-containers.md +0 -0
  60. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/cicd/references/maintenance-playbook.md +0 -0
  61. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/cicd/references/package-publishing.md +0 -0
  62. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/cicd/references/repository-setup.md +0 -0
  63. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/cicd/references/workflow-patterns.md +0 -0
  64. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/coredoc/SKILL.md +0 -0
  65. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/coredoc/agents/openai.yaml +0 -0
  66. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/coredoc/references/agent-skill.md +0 -0
  67. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/coredoc/references/core-doc-set.md +0 -0
  68. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/coredoc/references/llms-files.md +0 -0
  69. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/coredoc/references/maintenance-playbook.md +0 -0
  70. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/entity-observation/SKILL.md +0 -0
  71. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/entity-self-knowledge/SKILL.md +0 -0
  72. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/entity-self-knowledge/references/capability_map.md +0 -0
  73. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/meshvault-live-editing/SKILL.md +0 -0
  74. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/review/SKILL.md +0 -0
  75. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/review/agents/openai.yaml +0 -0
  76. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/review/references/reviewer-memory.md +0 -0
  77. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/uxreview/SKILL.md +0 -0
  78. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/uxreview/agents/openai.yaml +0 -0
  79. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/uxreview/references/reviewer-memory.md +0 -0
  80. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/registry/skills/verification-before-completion/SKILL.md +0 -0
  81. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/selection.py +0 -0
  82. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/tree.py +0 -0
  83. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/trust.py +0 -0
  84. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill/validation.py +0 -0
  85. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill.egg-info/dependency_links.txt +0 -0
  86. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill.egg-info/requires.txt +0 -0
  87. {abstractskill-0.3.3 → abstractskill-0.3.4}/src/abstractskill.egg-info/top_level.txt +0 -0
  88. {abstractskill-0.3.3 → abstractskill-0.3.4}/tests/test_audience.py +0 -0
  89. {abstractskill-0.3.3 → abstractskill-0.3.4}/tests/test_catalog.py +0 -0
  90. {abstractskill-0.3.3 → abstractskill-0.3.4}/tests/test_demand.py +0 -0
  91. {abstractskill-0.3.3 → abstractskill-0.3.4}/tests/test_loader.py +0 -0
  92. {abstractskill-0.3.3 → abstractskill-0.3.4}/tests/test_parser.py +0 -0
  93. {abstractskill-0.3.3 → abstractskill-0.3.4}/tests/test_policy.py +0 -0
  94. {abstractskill-0.3.3 → abstractskill-0.3.4}/tests/test_refresh_shelf_pins.py +0 -0
  95. {abstractskill-0.3.3 → abstractskill-0.3.4}/tests/test_selection.py +0 -0
  96. {abstractskill-0.3.3 → abstractskill-0.3.4}/tests/test_tree.py +0 -0
  97. {abstractskill-0.3.3 → abstractskill-0.3.4}/tests/test_trust.py +0 -0
  98. {abstractskill-0.3.3 → abstractskill-0.3.4}/tests/test_vendor_script.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: abstractskill
3
- Version: 0.3.3
3
+ Version: 0.3.4
4
4
  Summary: Agent Skills (SKILL.md) parsing, discovery, trust gating, and demand derivation for AbstractFramework
5
5
  Author-email: Laurent-Philippe Albou <contact@abstractcore.ai>
6
6
  License-Expression: MIT
@@ -61,12 +61,16 @@ pip install abstractskill
61
61
 
62
62
  The wheel carries the library and the curated skill registry (the shelf below).
63
63
 
64
+ What's new in 0.3.4: the `abstractframework-website` skill joins the shelf and
65
+ `abstract-release` covers AbstractAvatar and the AbstractAssistant lanes; see
66
+ the [changelog](CHANGELOG.md#034---2026-11-02).
67
+
64
68
  ## The bundled skill registry
65
69
 
66
70
  `pip install abstractskill` installs the reviewed shelf as package data:
67
71
 
68
72
  ```
69
- abstractskill/registry/skills/ # 15 curated skills (one folder per SKILL.md)
73
+ abstractskill/registry/skills/ # 16 curated skills (one folder per SKILL.md)
70
74
  abstractskill/registry/licenses/ # upstream licenses of catalog-vendored skills
71
75
  abstractskill/registry/validations.yaml # trust records: byte pins per skill tree
72
76
  abstractskill/registry/advisories.yaml # do-not-use advisories (empty at v1, by design)
@@ -28,12 +28,16 @@ pip install abstractskill
28
28
 
29
29
  The wheel carries the library and the curated skill registry (the shelf below).
30
30
 
31
+ What's new in 0.3.4: the `abstractframework-website` skill joins the shelf and
32
+ `abstract-release` covers AbstractAvatar and the AbstractAssistant lanes; see
33
+ the [changelog](CHANGELOG.md#034---2026-11-02).
34
+
31
35
  ## The bundled skill registry
32
36
 
33
37
  `pip install abstractskill` installs the reviewed shelf as package data:
34
38
 
35
39
  ```
36
- abstractskill/registry/skills/ # 15 curated skills (one folder per SKILL.md)
40
+ abstractskill/registry/skills/ # 16 curated skills (one folder per SKILL.md)
37
41
  abstractskill/registry/licenses/ # upstream licenses of catalog-vendored skills
38
42
  abstractskill/registry/validations.yaml # trust records: byte pins per skill tree
39
43
  abstractskill/registry/advisories.yaml # do-not-use advisories (empty at v1, by design)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "abstractskill"
7
- version = "0.3.3"
7
+ version = "0.3.4"
8
8
  description = "Agent Skills (SKILL.md) parsing, discovery, trust gating, and demand derivation for AbstractFramework"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -140,4 +140,4 @@ __all__ = [
140
140
  "validate_skill_name",
141
141
  ]
142
142
 
143
- __version__ = "0.3.3"
143
+ __version__ = "0.3.4"
@@ -16,7 +16,7 @@
16
16
  # four yaml files shipped in the abstractskill wheel). Bump it whenever any
17
17
  # of that content changes; seed_registry() records it in <dest>/.seeded.json
18
18
  # and tests/test_bundled.py pins it to a content digest.
19
- version: "2026.10.27"
19
+ version: "2026.11.02"
20
20
 
21
21
  skills:
22
22
 
@@ -5,8 +5,8 @@ description: Plan and run an AbstractFramework release wave across its packages
5
5
 
6
6
  # Abstract Release
7
7
 
8
- AbstractFramework is 34 packages in 22 repositories on 2026-10-27 (Python on PyPI, npm packages
9
- and apps, Rust crates on crates.io; `./scripts/deps.sh` in the workspace lists them). A release
8
+ AbstractFramework contains Python packages on PyPI, npm packages and apps, and Rust crates on
9
+ crates.io; `./scripts/deps.sh` lists the current workspace inventory. A release
10
10
  wave publishes the changed ones from the bottom of the dependency graph upward, so every package
11
11
  a parent depends on is already published, green, and visible on its registry before the parent
12
12
  raises its floor and publishes.
@@ -16,6 +16,38 @@ tag, publish), `coredoc` (README, docs, llms files, version source), `backlog` (
16
16
  its evidence) and `adr` (durable rules). This skill adds what only shows up across packages: the
17
17
  order, the gates between packages, floor propagation, and the root profile.
18
18
 
19
+ ## Read-only readiness audits
20
+
21
+ When asked whether to release, produce evidence and a recommendation without executing a release.
22
+ The checkout synchronization, checkpoint commits, branch integration and final build rules below
23
+ apply when release execution is authorized; an audit does not authorize pulling, merging, staging
24
+ operator edits, dispatching workflows, rerunning CI, or publishing. Read remote refs and CI through
25
+ read-only GitHub APIs when needed and label cached tracking refs explicitly.
26
+
27
+ Read `references/readiness-audit.md` for the audit evidence contract, registry-only build checks,
28
+ missing-package discovery and the distinction between a release blocker and later roadmap work.
29
+
30
+ ## Non-negotiable rules (operator, 2026-10-28; release execution)
31
+
32
+ 1. **A release ships EVERY pending change of the work in scope.** Before cutting any package: every
33
+ pending change ON `main` (local unpushed commits, uncommitted edits: an operator's edit is committed
34
+ as `checkpoint` after a secret scan, never left aside) and every branch THE AGENTS CREATED to complete
35
+ a task is merged into an up-to-date `main`, tested and released. NOT every branch: the operator's own
36
+ branches (e.g. a private memory-block branch in AbstractCore) are theirs, are never merged, pushed or
37
+ touched unless the operator says so; when in doubt about a branch's owner, ask. "Held because the operator has edits" or "held because local main has
38
+ unpushed commits" is forbidden: fix the build, merge with origin, release. The only reasons to stop
39
+ are secrets in the tree or a failing test that cannot be fixed without a decision: then ask, with
40
+ the exact file and failure, and keep every other package moving.
41
+ 2. **Always pull before starting any work**, in every repository, every time: `git -C <repo> fetch
42
+ origin && git -C <repo> pull --ff-only origin main` before cutting a branch or a worktree; cut
43
+ worktrees from `origin/main`. Pull the local `main` again right after every merge on GitHub.
44
+ 3. **A release means every pending change has been merged into an up-to-date `main`.** The wave ends
45
+ only when `./scripts/status.sh` shows every repository on `main`, clean, 0 ahead, 0 behind, and
46
+ `./scripts/build.sh` passes from those checkouts. A checkout left on a feature branch, a merged PR
47
+ not pulled, or a repo "N ahead" is a release defect, reported as such, not as a note.
48
+ 4. A worker that cannot run `git merge` or `git rebase` or `git switch` asks the coordinator to do it
49
+ immediately; it never leaves the branch unmerged and moves on.
50
+
19
51
  ## Before you start
20
52
 
21
53
  - Default to a read-only plan. Commits, pushes, tags, workflow dispatches, publishes, CI reruns and
@@ -31,6 +63,8 @@ python3 "$SKILL_DIR/scripts/abstract_release_plan.py" "$AF_ROOT" --registry
31
63
  python3 "$SKILL_DIR/scripts/abstract_release_plan.py" "$AF_ROOT" --registry --json -o "$AF_ROOT/untracked/release-plan.json"
32
64
  ```
33
65
 
66
+ Two packages may share a registry name across registries (AbstractAvatar's crate and Python
67
+ package): the planner keys the crate as `<name>-crate`; a crate dependent's edge resolves to it.
34
68
  Without a path, the planner finds the workspace by walking up from the current directory
35
69
  (or reads `ABSTRACTFRAMEWORK_ROOT`). A package is in the wave when it has uncommitted changes,
36
70
  commits since its last release tag, a version with no tag, or (with `--registry`) a version not
@@ -47,8 +81,11 @@ python3 "$SKILL_DIR/scripts/abstract_release_plan.py" "$AF_ROOT" --registry --js
47
81
  Release by published dependency floors, not by what happens to pass locally:
48
82
 
49
83
  1. Independent lower packages: `abstractvoice`, `abstractvision`, `abstractmusic` (and the other
50
- tier-0 media plugins), `abstractskill`, and the AbstractUIC npm packages (`ui-kit`,
51
- `app-server`, `monitor-*`, `panel-chat`).
84
+ tier-0 media plugins), `abstractskill`, `abstractavatar` (one repository, TWO packages: the
85
+ crate `abstractavatar` on crates.io and the Python package `abstractavatar` on PyPI; the crate
86
+ first: `abstracttui`'s `avatar` feature depends on it, and `abstractcore-console`,
87
+ `abstractgateway-console` and `abstractcode` depend on `abstracttui` with that feature), and
88
+ the AbstractUIC npm packages (`ui-kit`, `app-server`, `monitor-*`, `panel-chat`).
52
89
  2. `abstractsemantics` and `abstractmemory` (AbstractMemory owns the memory graph).
53
90
  3. `abstractcore`.
54
91
  4. `abstractruntime`.
@@ -56,8 +93,20 @@ Release by published dependency floors, not by what happens to pass locally:
56
93
  6. `abstractgateway`.
57
94
  7. Apps on the gateway: `abstractassistant`, `abstractcode`, `abstractobserver`, `abstractentity`,
58
95
  `abstractflow`, `abstractcontinuum`, `abstractnote` (Python on the gateway and AbstractMemory,
59
- plus its web app).
60
- 8. Root `abstractframework`: pins, `RELEASE_VERSIONS`, install manifest, installers, install docs.
96
+ plus its web app). AbstractAssistant is one repository with FOUR lanes: the Python package
97
+ (PyPI, the desktop palette and the gateway seam), the web app (`web/`, `@abstractframework/assistant`,
98
+ served by the gateway; not on npm yet), the iPhone app (`phone/iphone`, App Store / TestFlight
99
+ through `scripts/release.py` and `scripts/upload-candidate.sh`, `iphone.yml`) and the watch app
100
+ (`watch/apple`, `watch.yml`, `scripts/testflight.sh`). The registry wave releases the Python
101
+ package; the Apple lanes release through App Store Connect with the operator's approval
102
+ (`app-store` environment) and are reported as their own rows, never folded into the PyPI row.
103
+ 8. Root `abstractframework`: pins, `RELEASE_VERSIONS`, `NPM_RELEASE_VERSIONS`,
104
+ `CRATE_RELEASE_VERSIONS` (includes `abstractavatar` and `abstracttui`), install manifest,
105
+ installers, install docs.
106
+ 9. The website, last and gated: once the root profile is on PyPI, run the `abstractframework-website`
107
+ skill — it updates the site branch locally (versions, install commands, new apps and
108
+ capabilities, refreshed screenshots and llms mirrors), serves it locally and hands the operator
109
+ the preview URL. The operator validates and pushes `gh-pages`; a wave never pushes the site.
61
110
 
62
111
  The planner refines this from the real manifests: it skips packages with nothing to release and
63
112
  adds every package downstream of a changed package for a floor review. `scripts/lib/packages.txt`
@@ -207,3 +256,91 @@ to release):
207
256
  - `references/package-inventory.md`: the tiers, and per package its version source, release
208
257
  trigger, registry environment, llms generator, and package-specific rules.
209
258
  - `references/package-discovery.md`: how the planner discovers packages and reads its output.
259
+
260
+ ## Keep every checkout in step with GitHub (repo sync rule)
261
+
262
+ Observed 2026-10-27: several local `main` checkouts were dozens of commits behind their remotes
263
+ (abstractuic 86, the root 10) because PRs were merged on GitHub while nobody pulled the local
264
+ checkout, and new branches were then cut from the stale main. Rules, every time:
265
+
266
+ 1. Before cutting a branch or a worktree: `git -C <repo> fetch origin && git -C <repo> pull --ff-only origin main`
267
+ on the main checkout (a dirty checkout is left alone and reported; never stash someone else's work).
268
+ 2. After merging a PR on GitHub: pull the local main checkout immediately (the merge watcher does it).
269
+ 3. Worktrees are cut from `origin/main`, never from the local `main` ref.
270
+ 4. A sweep before any release wave: for each repo, `rev-list --count main..origin/main` must be 0;
271
+ `scripts/status.sh` at the root prints ahead/behind per repo.
272
+ 5. A root `main` that diverged (local release commits made elsewhere) is merged, never rebased: the
273
+ operator's commits keep their hashes; origin's pins and version win in conflicts, local docs and backlog
274
+ additions are kept, duplicated release sections are dropped.
275
+
276
+ ## Final gate: the local source build of the whole stack
277
+
278
+ After the root pins are released, run the operator's own developer path before the final report:
279
+ `./scripts/status.sh` (every repo on `main`, 0 behind, no checkout left on a feature branch by a worker)
280
+ and `./scripts/build.sh` (editable Python installs, every npm package built from local packs, every Rust
281
+ crate compiled tier by tier). A failure there is a release defect even when every registry install passes:
282
+ a crate that does not compile from a clean checkout, a pin that does not resolve locally, a checkout left
283
+ behind. Operator-modified files are reported, never built around: say which file fails and whose edit it is.
284
+
285
+ ## Final report: the summary table, then "show me your work"
286
+
287
+ The per-package summary table (package | released | new capabilities | remaining work) is the right
288
+ visibility level and stays. It is NOT the report. For every track of the wave add a "show me your work"
289
+ section with exactly three parts, written for the operator who was not watching:
290
+
291
+ - **(a) What was tested:** the suites and counts per package, the end-to-end runs on real weights or a
292
+ real gateway (what scenario, which model, which data), the mutation checks (which rule was removed and
293
+ which test went red), the adversarial review rounds and verdicts, the conformance fixtures (how many,
294
+ real vs synthetic).
295
+ - **(b) What it enables or fixes:** in the operator's words: the user-visible behaviour before and after,
296
+ and which apps/clients actually consume it today versus which still have to migrate (name each client
297
+ and its state: migrated / designed-for / untouched). Never present a served capability as integrated
298
+ when no client reads it.
299
+ - **(c) Proofs:** numbers a human can read: latency and memory before/after, sizes, counts, run ids,
300
+ PR links, registry versions, and one concrete example (a ledger, a run, a voice) walked end to end.
301
+
302
+ A track whose clients are not migrated is reported as "foundation released, integration pending" with the
303
+ migration list and an ETA, never folded into "done".
304
+
305
+ ## Think like an architect before closing anything (operator, 2026-10-28)
306
+
307
+ Before a package is held, a track is called done, or a wave is closed, the coordinator (or its adversarial
308
+ reviewer) answers these questions in writing:
309
+
310
+ - What happens if I do not release all the pending changes I have been working on? What breaks for the
311
+ person who installs or builds from source tomorrow?
312
+ - What happens if I do not release this package just yet? Which consumer is left on a stale floor?
313
+ - Did I fully implement AND reuse the functionality I just created in the consumers of that code?
314
+ (A shared component in abstractuic or a gateway contract exists only when the apps consume it:
315
+ AbstractCode, AbstractAssistant, AbstractObserver, AbstractContinuum, AbstractFlow, AbstractNote,
316
+ the consoles.) List each consumer with migrated / pending.
317
+ - Is the result production-grade for a person, not only green for CI?
318
+
319
+ The goal is always a finished, production-grade solution that benefits from the change; the secondary goal
320
+ is the supervisor's visibility: a roadmap while work is ongoing, and at the end a report that shows the
321
+ work (what is new or fixed, what it enables now or later, why it is good). Keep it a dialogue: ask early
322
+ when a decision is genuinely the operator's, decide and say so when it is not.
323
+
324
+ ## Backlog discipline for work left pending
325
+
326
+ When a task cannot be carried on now (time, credits, a blocker, a decision), write the backlog item
327
+ immediately, while the understanding is fresh: `proposed` when the need is likely, `planned` when it is
328
+ certain; it carries the analysis, the chosen route, the evidence so far and the open decision.
329
+ When an item is picked up later, start with an adversarial review of the item itself: is its knowledge
330
+ still current, is it still the best route (new research, superseded component, changed contract)?
331
+
332
+ ## Changelog fragments (gateway, core, runtime; since 2026-10-29)
333
+
334
+ These repos no longer edit `CHANGELOG.md [Unreleased]` per PR. Each PR adds `changelog.d/<pr-or-slug>.md`; CI fails a PR that touches source without a fragment. At release time, before the version bump commit:
335
+
336
+ ```
337
+ python scripts/changelog.py check # every fragment well-formed
338
+ python scripts/changelog.py preview # what the section will say
339
+ python scripts/changelog.py assemble X.Y.Z # writes the [X.Y.Z] section, deletes the fragments
340
+ ```
341
+
342
+ Commit the assembled section with the version bump. Never hand-edit `[Unreleased]` in these repos.
343
+
344
+ ## A conflicting PR gets no CI run
345
+
346
+ GitHub creates `pull_request` runs from the merge ref; a PR whose `mergeStateStatus` is DIRTY has none, so pushes to it create no run and no error. Before pushing a fix: merge `origin/main` into the branch, then `gh pr view N --json mergeStateStatus` must say CLEAN/BLOCKED, never DIRTY.
@@ -4,7 +4,7 @@ What exists, in which order it releases, and how each package releases. The work
4
4
  `scripts/lib/packages.txt` is the machine-readable inventory (`./scripts/deps.sh` prints the tiers,
5
5
  `./scripts/deps.sh check` verifies them against the real manifests, `./scripts/deps.sh rdeps <id>`
6
6
  lists what must follow a bump). This page adds the release mechanics. Facts were read from the
7
- repositories and their GitHub settings on 2026-10-27; re-check a row before relying on it for an
7
+ repositories and their GitHub settings on 2026-10-27 (AbstractAvatar and AbstractAssistant lanes: 2026-11-02); re-check a row before relying on it for an
8
8
  irreversible step.
9
9
 
10
10
  ## Tiers
@@ -14,8 +14,8 @@ depends on is published and visible.
14
14
 
15
15
  | Tier | Python (PyPI) | npm | Rust (crates.io) |
16
16
  |---|---|---|---|
17
- | 0 | abstractskill, abstractsemantics, AbstractMemory, abstractvision, abstractvoice, abstractmusic, abstractcamera | ui-kit, app-server, monitor-flow, monitor-gpu, monitor-memory, monitor-active-memory | abstracttui |
18
- | 1 | abstract3d (needs abstractvision) | panel-chat (peer of ui-kit) | abstracttui-graph, abstractcore-console, abstractcode (CLI) |
17
+ | 0 | abstractskill, abstractsemantics, AbstractMemory, abstractvision, abstractvoice, abstractmusic, abstractcamera, abstractavatar (Python) | ui-kit, app-server, monitor-flow, monitor-gpu, monitor-memory, monitor-active-memory | abstractavatar (crate) |
18
+ | 1 | abstract3d (needs abstractvision) | panel-chat (peer of ui-kit) | abstracttui (its `avatar` feature needs the abstractavatar crate), abstracttui-graph, abstractcore-console, abstractcode (CLI) |
19
19
  | 2 | abstractcore (needs voice, vision, music, 3d) | flow, observer, continuum, entity, code (web), note (web) | abstracttui-mermaid, abstractgateway-console |
20
20
  | 3 | AbstractRuntime (needs core, memory, semantics) | | |
21
21
  | 4 | abstractagent, abstractassistant | | |
@@ -35,6 +35,13 @@ Roles worth knowing when you order a wave:
35
35
  its `gateway` extra) plus a web app (`@abstractframework/note`). The gateway imports
36
36
  `abstractnote` lazily as an optional seam, so it is not a gateway dependency; AbstractNote
37
37
  releases after the gateway it needs.
38
+ - **AbstractAvatar** is one repository with two packages that must stay at one version: the
39
+ crate (procedural avatars, the Wasm artifact) and the Python package over it. The crate releases
40
+ first (AbstractTUI's `avatar` feature, hence every console crate, resolves it from crates.io);
41
+ the Python package follows through its `release.yml`.
42
+ - **AbstractAssistant** is one repository with four lanes: the Python package (PyPI), the web app
43
+ (`web/`, served by the gateway), the iPhone app and the watch app (App Store Connect through the
44
+ `app-store` environment, operator approval). Only the Python package is a registry release.
38
45
  - **abstractframework** (root) pins exact versions of the released matrix and releases last.
39
46
 
40
47
  Not released with the framework: the local test projects `abstractaudio`, `abstractsound`,
@@ -67,6 +74,7 @@ release needs a tag rule there.
67
74
  | Package | Repo | Version source | Trigger | `pypi` env policy | llms generator | Notes |
68
75
  |---|---|---|---|---|---|---|
69
76
  | abstractvoice | lpalbou/AbstractVoice | `abstractvoice/_version.py` (single source; pyproject reads it) | tag | main, tag `v*.*.*` | none (edit by hand) | PR CI: tests 3.9-3.12, Qwen3-TTS/ASR transformers floor and latest, build, docs (10 checks). release.yml publishes to PyPI with trusted publishing, creates the GitHub release, deploys docs. |
77
+ | abstractavatar (Python) | lpalbou/AbstractAvatar | `pyproject.toml` (= `Cargo.toml` version) | tag `v*` or dispatch `-f version=X.Y.Z -f publish_confirmation=…` (read `release.yml`: created 2026-11-02) | `pypi` env, trusted publisher registered by the operator 2026-11-02 (project `abstractavatar`, workflow `release.yml`, environment `pypi`) | `scripts/docs.py` | Builds the native wheel matrix + sdist; the crate is NOT published by this workflow. |
70
78
  | abstractvision | lpalbou/AbstractVision | `src/abstractvision/__init__.py` | tag | no restriction | `scripts/generate_llms_full.py` | |
71
79
  | abstractmusic | lpalbou/AbstractMusic | `src/abstractmusic/_version.py` | tag or dispatch (`-f publish=true -f publish_confirmation=publish-abstractmusic-X.Y.Z`) | main, tag `v*.*.*` | none | |
72
80
  | abstractcamera | lpalbou/AbstractCamera | `src/abstractcamera/__init__.py` | tag | main, tag `v*` | `scripts/generate_llms_full.py` | Ships a package agent skill (`skills/camera-piloting/`). |
@@ -98,6 +106,7 @@ release needs a tag rule there.
98
106
 
99
107
  | Crate | Repo / path | Trigger | `crates-io` env policy |
100
108
  |---|---|---|---|
109
+ | abstractavatar | lpalbou/AbstractAvatar, `.` | local `cargo publish` from the tagged commit (no crates.io workflow; `~/.cargo/credentials.toml` on the operator's machine); tag `v*` | none | First published 0.1.0 on 2026-11-02; the `avatar` feature of abstracttui resolves it from crates.io, never from a path. |
101
110
  | abstracttui | lpalbou/AbstractTUI | tag `v*`, or dispatch `-f publish=true` (without it the run only verifies) | no restriction |
102
111
  | abstracttui-graph, abstracttui-mermaid | lpalbou/AbstractTUI, `extensions/graph`, `extensions/mermaid` | with abstracttui: the `publish-family` job publishes graph, then mermaid (mermaid depends on graph) | no restriction |
103
112
  | abstractcore-console | lpalbou/AbstractCore, `console-tui/` | with AbstractCore | no restriction |
@@ -0,0 +1,54 @@
1
+ # Release readiness audit
2
+
3
+ An audit answers what is published, what changed, what must ship together, and what evidence is
4
+ still missing. It does not execute the wave. Preserve existing operator rules when refreshing
5
+ this skill from AbstractSkill: compare all files first, because the registry copy may be older
6
+ than the installed skill.
7
+
8
+ ## Evidence to collect
9
+
10
+ - Record the observation time, local HEAD, branch and dirty state for every inventoried repo.
11
+ Query live remote-main SHAs when available; cached `origin/main` alone cannot prove current
12
+ ahead/behind. Do not pull or merge during the audit.
13
+ - Run the planner with registry checks. Compare published versions, matching release tags and
14
+ the source changes beyond those tags. A published local version with newer source requires a
15
+ fresh version; publication of the old version is not proof that HEAD shipped.
16
+ - Review open release-scope agent PRs as well as main. A clean checkout can still omit delivered
17
+ work on a branch. Protect operator branches and do not infer ownership from branch names alone.
18
+ - Check CI for the proposed commit, not merely the latest successful run. Keep failed jobs,
19
+ pending checks, environment failures and missing checks distinct. Inspect failed-job evidence
20
+ before classifying a failure or recommending a rerun.
21
+ - Check root pins, installer matrix and per-package dependency floors. Internal consistency of
22
+ the old root matrix does not show that it delivers the new capabilities.
23
+ - Reconcile backlog claims against code, completed records, registry data and CI. Count lifecycle
24
+ files separately from unique tickets; planned/completed duplicates and old overview text are
25
+ not reliable estimates of remaining work.
26
+
27
+ ## Check the artifacts users install
28
+
29
+ Local packs, editable siblings and path dependencies can hide missing published APIs. For a
30
+ consumer using new shared UI exports, perform a clean source build in a temporary directory with
31
+ dependencies resolved from the registry. A version-only lockfile check can pass while the build
32
+ fails because a locally packed dependency reused an already-published version number.
33
+
34
+ Inspect planner `inventory_gaps` even when `deps.sh check` succeeds. A newly added public crate
35
+ can be absent from the inventory and release workflow. Cargo `path` plus `version` dependencies
36
+ must resolve from crates.io when packaged: release and verify the new lower crate before the
37
+ dependent package's `cargo publish --dry-run` verification job. Check its trusted publisher and
38
+ the workflow graph; adding a late publish job after verification does not solve this dependency.
39
+
40
+ Run applicable changelog fragment validation as a read-only check. Do not assemble fragments or
41
+ bump versions during an audit. A malformed fragment can block release preparation even when a
42
+ previous remote commit passed CI.
43
+
44
+ ## Verdict and sequencing
45
+
46
+ Separate observed defects, missing required proof and later product work. A partial consumer
47
+ migration is a foundation delivered with integration pending, not a complete feature. Security
48
+ guards, registry-resolvable dependencies, required concurrency/isolation evidence and exact-commit
49
+ CI are release gates when applicable; unrelated proposals do not all become blockers.
50
+
51
+ Return a per-package release/skip/floor-review decision, proposed fresh versions (subject to a
52
+ new registry check), dependency order, and exit criteria. Give effort ranges as estimates with
53
+ assumptions, never promise a date without evidence. Preserve recorded release-scope obligations;
54
+ do not silently defer already-delivered main changes to manufacture a smaller green wave.
@@ -608,7 +608,12 @@ def collect_requirements(
608
608
  for group, raw in raw_groups:
609
609
  req = parse_requirement(raw, group, known)
610
610
  # A crate only depends on crates; a Python/npm name that compacts to a
611
- # crate name (or the reverse) is a different package.
611
+ # crate name (or the reverse) is a different package. A name shared by a
612
+ # crate and a Python package resolves to the crate for a crate dependent.
613
+ if req and pkg.ecosystem == "crates" and by_key_ecosystem(packages, req.target) != "crates":
614
+ alt = known.get(req.target + "-crate")
615
+ if alt is not None:
616
+ req.target = alt
612
617
  if req and (pkg.ecosystem == "crates") != (by_key_ecosystem(packages, req.target) == "crates"):
613
618
  continue
614
619
  add_internal_requirement(pkg, req)
@@ -623,6 +628,8 @@ def collect_requirements(
623
628
  by_key = {p.key: p for p in packages}
624
629
  for var in ("NPM_RELEASE_VERSIONS", "CRATE_RELEASE_VERSIONS"):
625
630
  for key, version in parse_version_dict(init_path, var).items():
631
+ if var == "CRATE_RELEASE_VERSIONS" and key + "-crate" in by_key:
632
+ key = key + "-crate"
626
633
  if key in by_key:
627
634
  add_internal_requirement(
628
635
  framework, InternalRequirement(key, f"{by_key[key].name}=={version}", var, "==", version)
@@ -778,6 +785,18 @@ def discover(root: Path) -> Tuple[List[Package], List[Dict[str, Any]], str]:
778
785
  keys: Dict[str, Package] = {}
779
786
  for pkg in packages:
780
787
  if pkg.key in keys:
788
+ # One repository can publish a crate AND a Python package under the same
789
+ # name (AbstractAvatar: crates.io `abstractavatar` + PyPI `abstractavatar`).
790
+ # They are different packages on different registries: the crate keys as
791
+ # `<name>-crate` so requirements and pins can tell them apart.
792
+ other = keys[pkg.key]
793
+ crate = pkg if pkg.ecosystem == "crates" else other if other.ecosystem == "crates" else None
794
+ if crate is not None and {pkg.ecosystem, other.ecosystem} != {"crates"}:
795
+ keys.pop(crate.key, None)
796
+ crate.key = crate.key + "-crate"
797
+ for remaining in (pkg, other):
798
+ keys.setdefault(remaining.key, remaining)
799
+ continue
781
800
  raise PlanError(f"duplicate package {pkg.name}: {keys[pkg.key].path} and {pkg.path}")
782
801
  keys[pkg.key] = pkg
783
802
  collect_git_status(packages)
@@ -0,0 +1,110 @@
1
+ ---
2
+ name: abstractframework-website
3
+ description: Update the abstractframework.ai website after a release wave so it describes the framework that is actually published (versions, install commands, every app and capability, refreshed screenshots and llms mirrors), verify every claim against code and registries, serve the result locally and hand the operator a preview URL. The site is GATED - this skill never pushes gh-pages; the operator validates the preview and publishes. Use at the end of every abstract-release wave, when the operator asks whether the site is current, or when a page describes a package that moved.
4
+ ---
5
+
6
+ # AbstractFramework website update (gated)
7
+
8
+ The site at https://abstractframework.ai is the `gh-pages` branch of lpalbou/AbstractFramework:
9
+ static HTML (`index.html`, one page per package and per persona, `llms*.txt` mirrors, screenshots
10
+ under `assets/`). Its job is to describe the framework a person can install TODAY. After every
11
+ release wave it must say the released versions, the install commands that resolve, every app that
12
+ exists (desktop, web, iPhone, watch, consoles), every capability (text, voice, vision, music, 3D,
13
+ camera, memory, decisions, entities, automations) and what changed, in the operator's voice.
14
+
15
+ **The gate.** This skill ends with a local preview URL and a branch, never with a push. The
16
+ operator reads the preview, asks for changes, and pushes `gh-pages` personally. A worker that
17
+ pushes, force-pushes, or rebases `gh-pages` has broken the operator's one rule for this surface.
18
+
19
+ ## Paths
20
+
21
+ - Site worktree: `/Users/albou/tmp/abstractframework-ghpages` (a git worktree of the root repo on
22
+ a site branch). Published ref: `origin/gh-pages`. Start every update from it:
23
+ `git -C <worktree> fetch origin && git -C <worktree> switch -c site-<root version> origin/gh-pages`
24
+ (one branch per root version; an existing branch for the same version is resumed, never recreated).
25
+ - Method and history: `/Users/albou/tmp/abstractframework/untracked/site-rethink/` (PLAN.md,
26
+ REPORT.md with the operator's rounds, CLAIMS.md, examples/, deliver/, shots/). Read REPORT.md's
27
+ operator rounds before changing copy: they are the record of what was accepted and refused.
28
+ - Preview server: `python3 references/preview_server.py <worktree> --port 56265` (Range-capable,
29
+ needed for the videos). The URL to hand over is `http://127.0.0.1:56265/`.
30
+ - Workspace: `/Users/albou/tmp/abstractframework/<repo>` (each its own git repo; `git -C`).
31
+
32
+ ## Invariants (operator decisions; keep them unless the operator changes them on the record)
33
+
34
+ - Identity: the original dark site; content changes, identity does not. Tagline
35
+ "Write once. Generate everything. Run anywhere." Modality order voice, music, vision, 3D, camera.
36
+ - Install: exactly three profiles, `abstractframework`, `abstractframework[apple]`,
37
+ `abstractframework[gpu]`, plus the short installer addresses (`abstractframework.ai/install.sh`,
38
+ `/install.ps1`, `/uninstall.sh`). Never `[remote]`, `[mlx]` or a per-package pip line as the
39
+ primary path. Provider badges list every provider the text names, never trimmed.
40
+ - Names: PascalCase package names in prose (AbstractCore, AbstractAssistant); "Consoles" is not
41
+ a package; the Assistant is "One Click Away", never "Built for macOS" (OS-agnostic, tested most on
42
+ Apple). Entities are "persistent, self-evolving agents ... we are defining the field here", the
43
+ identity planted once; memory is reconstruction (passive / active / durable).
44
+ - The "Ledger Is the Record of the Run" animation stays as it is.
45
+ - Every code block is either RUN (a hermetic example under `untracked/site-rethink/examples/`,
46
+ its `run.log` next to it, the HTML comment `E<n> PASS`) or carries a visible "not executed" note.
47
+ No worker runs model generation for the site beyond what an example needs; one model process at
48
+ a time on the machine (the live gateway owns the GPU).
49
+ - Screenshots come from a fresh scratch install of the RELEASED matrix (never from a dev tree),
50
+ captured at the sizes the page uses, including a 400 px phone capture for every new page or
51
+ redesigned interface. A pre-release capture is captioned as such.
52
+ - Claims are text-only when the code says so: the adversary of 2026-09-29 found the operator's own
53
+ framings false in code (no hash-chained gateway ledgers, no re-execution on replay, `create_llm`
54
+ needs a provider). Verify before writing, write what the code does.
55
+
56
+ ## Procedure
57
+
58
+ 1. **Inventory what is published.** `python3 ~/.codex/skills/abstract-release/scripts/abstract_release_plan.py <workspace> --registry`
59
+ plus the root's `abstractframework/__init__.py` (`RELEASE_VERSIONS`, `NPM_RELEASE_VERSIONS`,
60
+ `CRATE_RELEASE_VERSIONS`) and `pip download abstractframework==<v> --no-deps` for the pins.
61
+ The site states the ROOT version and the versions the root pins; nothing "coming".
62
+ 2. **Diff the site against reality.** For every page: the versions it names, the install lines,
63
+ the package list, the apps and capabilities it describes, the screenshots' provenance, the
64
+ llms mirrors' headers. `grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+" *.html | sort | uniq -c` finds stale
65
+ numbers fast. Read each released package's CHANGELOG from the version the site names to the
66
+ released one: that is the list of new features and fixes to tell.
67
+ 3. **Write the update ledger first** (`untracked/site-<root version>/CLAIMS.md`, same shape as the
68
+ 2026-09-29 ledger): every factual claim to add or change, with the repository line, registry
69
+ JSON, or example run that supports it; every page to touch; every screenshot to refresh and
70
+ from which scratch install. New apps get their own section or page (the Assistant's web, iPhone
71
+ and watch apps; a new console); a new capability gets its place in the home grid and on the
72
+ package page that provides it (decision models on core.html, with the model family named as the
73
+ package docs name it).
74
+ 4. **Apply the changes** on the site branch, in commits named `site <root version>: <what>`.
75
+ Refresh `llms.txt` / `llms-full.txt` mirrors from each released repository's main at the tag
76
+ (headers kept). Regenerate `sitemap.xml` when pages are added.
77
+ 5. **Check.** `python3 references/check_site.py <worktree>`: every local link and asset resolves,
78
+ every page has one `<title>`, no leftover `TODO`/`PENDING` markers, every `<img>` exists and has
79
+ `width`/`height`, no version string older than the root's pins unless the ledger explains it, no
80
+ "coming soon". Open every changed page in the preview at desktop width and 400 px and look at it;
81
+ record a screenshot of each under `untracked/site-<root version>/shots/`.
82
+ 6. **Adversarial pass.** A reader that did not write the pages checks the ledger line by line
83
+ against the code and the registries, and the copy against the invariants above; fix or
84
+ withdraw every finding on the record (`REVIEW-<n>.md`).
85
+ 7. **Serve and hand over.** Start the preview server on the worktree, confirm it answers, and
86
+ end with the handover block below. Do not push. Do not merge into `gh-pages`. Leave the
87
+ worktree on the site branch so the operator's rounds continue there.
88
+
89
+ ## Handover block (the last thing a run prints)
90
+
91
+ ```
92
+ Site branch: site-<root version> @ <sha> (worktree /Users/albou/tmp/abstractframework-ghpages)
93
+ Preview: http://127.0.0.1:56265/ (server pid <pid>; restart: python3 ~/.codex/skills/abstractframework-website/references/preview_server.py <worktree> --port 56265)
94
+ Changed pages: <list> New pages: <list> Screenshots refreshed: <n> (from abstractframework==<v> scratch install)
95
+ Ledger: untracked/site-<root version>/CLAIMS.md Review: REVIEW-1.md (<n> findings, <m> fixed)
96
+ Not done / needs you: <anything the operator must decide or capture>
97
+ To publish (operator only): git -C <worktree> push origin site-<root version>:gh-pages
98
+ ```
99
+
100
+ ## Pitfalls
101
+
102
+ - A site built from the dev checkouts drifts from what `pip install` gives: scratch-install the
103
+ released root profile for screenshots and example runs (`uv venv` + `uv pip install
104
+ abstractframework[apple]==<v>` with `UV_CACHE_DIR=/Users/albou/.cache/uv`; delete the venv after).
105
+ - The home page is one 180 KB file with inline sections per package: edit the section, not the
106
+ whole file; keep `assets/` names stable so the operator's cached screenshots stay valid.
107
+ - `llms-full.txt` mirrors are large; refresh them from the released tag, not from a working tree
108
+ that carries unreleased docs.
109
+ - A page for an app that exists on one platform only says so plainly (the watch app is watchOS,
110
+ the iPhone app iOS, the web app runs where the gateway runs).
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env python3
2
+ """Static checks for the abstractframework.ai site worktree. Read-only.
3
+
4
+ Usage: check_site.py <site dir> [--min-version-for <package>=<x.y.z> ...]
5
+ Fails (exit 1) on: a local link or asset that does not resolve, a page without exactly one
6
+ <title>, an <img> without width/height or whose file is missing, leftover TODO/PENDING/COMING SOON
7
+ markers, and version strings below the given floors next to a package name.
8
+ """
9
+ from __future__ import annotations
10
+
11
+ import argparse
12
+ import os
13
+ import re
14
+ import sys
15
+ from html.parser import HTMLParser
16
+
17
+
18
+ class Collector(HTMLParser):
19
+ def __init__(self) -> None:
20
+ super().__init__()
21
+ self.titles = 0
22
+ self.links: list[str] = []
23
+ self.imgs: list[tuple[str, bool]] = []
24
+
25
+ def handle_starttag(self, tag, attrs): # noqa: ANN001
26
+ d = dict(attrs)
27
+ if tag == "title":
28
+ self.titles += 1
29
+ if tag in ("a", "link") and d.get("href"):
30
+ self.links.append(d["href"])
31
+ if tag in ("script", "source", "video", "audio") and d.get("src"):
32
+ self.links.append(d["src"])
33
+ if tag == "img" and d.get("src"):
34
+ self.imgs.append((d["src"], bool(d.get("width") and d.get("height"))))
35
+
36
+
37
+ def local(ref: str) -> str | None:
38
+ if re.match(r"^(https?:|mailto:|tel:|data:|javascript:|#)", ref):
39
+ return None
40
+ return ref.split("#", 1)[0].split("?", 1)[0] or None
41
+
42
+
43
+ def main() -> int:
44
+ ap = argparse.ArgumentParser()
45
+ ap.add_argument("site")
46
+ ap.add_argument("--min-version-for", action="append", default=[], metavar="PKG=X.Y.Z")
47
+ a = ap.parse_args()
48
+ root = os.path.abspath(a.site)
49
+ floors = dict(x.split("=", 1) for x in a.min_version_for)
50
+ problems: list[str] = []
51
+ pages = [f for f in os.listdir(root) if f.endswith(".html")]
52
+ for page in sorted(pages):
53
+ text = open(os.path.join(root, page), encoding="utf-8", errors="replace").read()
54
+ c = Collector()
55
+ c.feed(text)
56
+ if c.titles != 1:
57
+ problems.append(f"{page}: {c.titles} <title> tags")
58
+ for marker in ("TODO", "PENDING-W", "COMING SOON", "coming soon", "FIXME"):
59
+ if marker in text:
60
+ problems.append(f"{page}: leftover marker {marker!r}")
61
+ for ref in c.links:
62
+ rel = local(ref)
63
+ if rel and not os.path.exists(os.path.join(root, rel.lstrip("/"))):
64
+ problems.append(f"{page}: broken local link {ref}")
65
+ for src, sized in c.imgs:
66
+ rel = local(src)
67
+ if rel and not os.path.exists(os.path.join(root, rel.lstrip("/"))):
68
+ problems.append(f"{page}: missing image {src}")
69
+ if not sized:
70
+ problems.append(f"{page}: <img {src}> without width/height")
71
+ for pkg, floor in floors.items():
72
+ for m in re.finditer(re.escape(pkg) + r"[^0-9\n]{0,40}?(\d+\.\d+\.\d+)", text):
73
+ found = tuple(int(x) for x in m.group(1).split("."))
74
+ if found < tuple(int(x) for x in floor.split(".")):
75
+ problems.append(f"{page}: {pkg} {m.group(1)} is below the released {floor}")
76
+ for p in problems:
77
+ print(p)
78
+ print(f"{len(pages)} pages, {len(problems)} problems")
79
+ return 1 if problems else 0
80
+
81
+
82
+ if __name__ == "__main__":
83
+ sys.exit(main())
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env python3
2
+ """Serve a static site directory locally with HTTP Range support (videos seek), no caching.
3
+
4
+ Usage: preview_server.py <directory> [--port 56265] [--bind 127.0.0.1]
5
+ Read-only: it never writes into the directory. Stop it with Ctrl-C or kill <pid>.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ import argparse
10
+ import os
11
+ import re
12
+ import sys
13
+ from functools import partial
14
+ from http import HTTPStatus
15
+ from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
16
+
17
+
18
+ class RangeHandler(SimpleHTTPRequestHandler):
19
+ def end_headers(self) -> None:
20
+ self.send_header("Cache-Control", "no-store")
21
+ self.send_header("Accept-Ranges", "bytes")
22
+ super().end_headers()
23
+
24
+ def send_head(self): # noqa: ANN201 - stdlib signature
25
+ rng = self.headers.get("Range")
26
+ if not rng:
27
+ return super().send_head()
28
+ path = self.translate_path(self.path)
29
+ if os.path.isdir(path):
30
+ return super().send_head()
31
+ try:
32
+ size = os.path.getsize(path)
33
+ except OSError:
34
+ self.send_error(HTTPStatus.NOT_FOUND, "File not found")
35
+ return None
36
+ m = re.match(r"bytes=(\d*)-(\d*)$", rng.strip())
37
+ if not m:
38
+ return super().send_head()
39
+ start = int(m.group(1)) if m.group(1) else max(0, size - int(m.group(2) or 0))
40
+ end = int(m.group(2)) if m.group(1) and m.group(2) else size - 1
41
+ end = min(end, size - 1)
42
+ if start > end or start >= size:
43
+ self.send_response(HTTPStatus.REQUESTED_RANGE_NOT_SATISFIABLE)
44
+ self.send_header("Content-Range", f"bytes */{size}")
45
+ self.end_headers()
46
+ return None
47
+ fh = open(path, "rb")
48
+ fh.seek(start)
49
+ self.send_response(HTTPStatus.PARTIAL_CONTENT)
50
+ self.send_header("Content-Type", self.guess_type(path))
51
+ self.send_header("Content-Range", f"bytes {start}-{end}/{size}")
52
+ self.send_header("Content-Length", str(end - start + 1))
53
+ self.end_headers()
54
+ self._range_left = end - start + 1
55
+ return fh
56
+
57
+ def copyfile(self, source, outputfile) -> None: # noqa: ANN001 - stdlib signature
58
+ left = getattr(self, "_range_left", None)
59
+ if left is None:
60
+ return super().copyfile(source, outputfile)
61
+ while left > 0:
62
+ chunk = source.read(min(65536, left))
63
+ if not chunk:
64
+ break
65
+ outputfile.write(chunk)
66
+ left -= len(chunk)
67
+ self._range_left = None
68
+
69
+ def log_message(self, fmt, *args) -> None: # noqa: ANN001 - quiet by default
70
+ if os.environ.get("PREVIEW_VERBOSE"):
71
+ super().log_message(fmt, *args)
72
+
73
+
74
+ def main() -> int:
75
+ ap = argparse.ArgumentParser()
76
+ ap.add_argument("directory")
77
+ ap.add_argument("--port", type=int, default=56265)
78
+ ap.add_argument("--bind", default="127.0.0.1")
79
+ a = ap.parse_args()
80
+ directory = os.path.abspath(a.directory)
81
+ if not os.path.isdir(directory):
82
+ print(f"not a directory: {directory}", file=sys.stderr)
83
+ return 2
84
+ handler = partial(RangeHandler, directory=directory)
85
+ httpd = ThreadingHTTPServer((a.bind, a.port), handler)
86
+ print(f"serving {directory} at http://{a.bind}:{a.port}/ (pid {os.getpid()})", flush=True)
87
+ try:
88
+ httpd.serve_forever()
89
+ except KeyboardInterrupt:
90
+ pass
91
+ return 0
92
+
93
+
94
+ if __name__ == "__main__":
95
+ sys.exit(main())
@@ -8,16 +8,16 @@
8
8
  validations:
9
9
  - name: abstract-release
10
10
  source: codex-skills (maintainer)
11
- tree_hash: f03e111097d8a8e1b8c39cb1e7c63bb9ad116e909b8b54478929274995c0b41b
11
+ tree_hash: e8c297c3c8a73d54a68fd754f789a54095ee701a8e36049de433342425e741a5
12
12
  level: adopted
13
13
  method: first-party-adoption
14
14
  audience: host
15
15
  validated_by: skill
16
- validated_at: '2026-10-27'
16
+ validated_at: '2026-11-02'
17
17
  evidence:
18
18
  parser_roundtrip: true
19
19
  has_scripts: true
20
- files: 7
20
+ files: 8
21
21
  notes: "Maintainer-authored skill (AbstractFramework release waves), rewritten 2026-10-27\
22
22
  \ from the release wave of that day: dependency order with AbstractNote after\
23
23
  \ the gateway, per-package mechanics (tag push vs workflow_dispatch, GitHub publishing-environment\
@@ -31,8 +31,19 @@ validations:
31
31
  \ requires_review at the gate until the operator enables them. Adversarially reviewed\
32
32
  \ before pinning (3 P0 / 6 P1 / 5 P2 folded: committed-but-unpublished detection,\
33
33
  \ per-workflow dispatch inputs, AbstractCode/Abstract3D triggers, scratch-gateway\
34
- \ recipe verified live). Behavioral audit (backlog 0003) pending \u2014 upgrades\
35
- \ to audited then."
34
+ \ recipe verified live). Re-reviewed and re-pinned 2026-11-02 from the maintainer\
35
+ \ copy: abstractavatar (crate first, then the Python package) in the order, AbstractAssistant's\
36
+ \ four lanes (PyPI, web, iPhone, watch; Apple lanes through the app-store environment),\
37
+ \ a gated step 9 handing the website to abstractframework-website (never pushes\
38
+ \ gh-pages), the operator's release-execution and repo-sync rules, the final local-build\
39
+ \ gate and 'show me your work' report, changelog fragments; NEW references/readiness-audit.md\
40
+ \ (read-only audit evidence contract); package-inventory rows for AbstractAvatar\
41
+ \ and the Assistant lanes. PLANNER PATCHED: a crate and a Python package sharing\
42
+ \ a name (abstractavatar) no longer abort with 'duplicate package' \u2014 the\
43
+ \ crate keys as <name>-crate, a crate dependent's edge resolves to it, CRATE_RELEASE_VERSIONS\
44
+ \ pins map to it (verified on the live workspace: 36 packages, abstractavatar-crate\
45
+ \ ordered before abstracttui; the previous planner aborts). Still read-only, stdlib\
46
+ \ only. Behavioral audit (backlog 0003) pending \u2014 upgrades to audited then."
36
47
  - name: abstractframework-gateway
37
48
  source: first-party
38
49
  tree_hash: bf438ba23526abf39e591865358c55ae8efa4da61437c4fc5527bafc68c6882d
@@ -58,6 +69,29 @@ validations:
58
69
  \ text) and close was taught as loop-release \u2014 re-taught to auto-wake-on-open\
59
70
  \ + restore-previous-on-close, refusal reasons verified against entity_visits.py\
60
71
  \ (paused/one-visit/consent-rite; sleep wakes)."
72
+ - name: abstractframework-website
73
+ source: first-party
74
+ tree_hash: b91fe3fb6d167fee6f535c5d9004669233b978c822b6ccd8054b2382eae5d0d5
75
+ level: first_party
76
+ method: first-party
77
+ audience: host
78
+ validated_by: skill
79
+ validated_at: '2026-11-02'
80
+ evidence:
81
+ parser_roundtrip: true
82
+ has_scripts: true
83
+ files: 3
84
+ notes: "First-party skill (2026-11-02): update the abstractframework.ai site (the\
85
+ \ gh-pages branch of lpalbou/AbstractFramework) after a release wave \u2014 inventory\
86
+ \ what is published, diff the site against reality, a claims ledger before any\
87
+ \ copy change, apply on a site-<root version> branch, check, adversarial pass,\
88
+ \ serve locally and hand the operator a preview URL. GATED: the skill never pushes,\
89
+ \ force-pushes, rebases or merges gh-pages; the operator validates the preview\
90
+ \ and publishes. SCRIPTS-BEARING (two read-only stdlib helpers under references/):\
91
+ \ preview_server.py (static server with HTTP Range support, binds 127.0.0.1 by\
92
+ \ default, no writes) and check_site.py (static link/asset/title/marker/version-floor\
93
+ \ checker, no writes, no network), so requires_review at the gate until the operator\
94
+ \ enables them. Paths name the maintainer's workspace."
61
95
  - name: adr
62
96
  source: codex-skills (maintainer)
63
97
  tree_hash: a4cb55fcea54e969a9c342e314b4dda3556ce5e6fb0e210821350688c62c934b
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: abstractskill
3
- Version: 0.3.3
3
+ Version: 0.3.4
4
4
  Summary: Agent Skills (SKILL.md) parsing, discovery, trust gating, and demand derivation for AbstractFramework
5
5
  Author-email: Laurent-Philippe Albou <contact@abstractcore.ai>
6
6
  License-Expression: MIT
@@ -61,12 +61,16 @@ pip install abstractskill
61
61
 
62
62
  The wheel carries the library and the curated skill registry (the shelf below).
63
63
 
64
+ What's new in 0.3.4: the `abstractframework-website` skill joins the shelf and
65
+ `abstract-release` covers AbstractAvatar and the AbstractAssistant lanes; see
66
+ the [changelog](CHANGELOG.md#034---2026-11-02).
67
+
64
68
  ## The bundled skill registry
65
69
 
66
70
  `pip install abstractskill` installs the reviewed shelf as package data:
67
71
 
68
72
  ```
69
- abstractskill/registry/skills/ # 15 curated skills (one folder per SKILL.md)
73
+ abstractskill/registry/skills/ # 16 curated skills (one folder per SKILL.md)
70
74
  abstractskill/registry/licenses/ # upstream licenses of catalog-vendored skills
71
75
  abstractskill/registry/validations.yaml # trust records: byte pins per skill tree
72
76
  abstractskill/registry/advisories.yaml # do-not-use advisories (empty at v1, by design)
@@ -31,10 +31,14 @@ src/abstractskill/registry/skills/abstract-release/SKILL.md
31
31
  src/abstractskill/registry/skills/abstract-release/agents/openai.yaml
32
32
  src/abstractskill/registry/skills/abstract-release/references/package-discovery.md
33
33
  src/abstractskill/registry/skills/abstract-release/references/package-inventory.md
34
+ src/abstractskill/registry/skills/abstract-release/references/readiness-audit.md
34
35
  src/abstractskill/registry/skills/abstract-release/references/release-process.md
35
36
  src/abstractskill/registry/skills/abstract-release/scripts/abstract_release_plan.py
36
37
  src/abstractskill/registry/skills/abstract-release/scripts/pypi_visibility_gate.py
37
38
  src/abstractskill/registry/skills/abstractframework-gateway/SKILL.md
39
+ src/abstractskill/registry/skills/abstractframework-website/SKILL.md
40
+ src/abstractskill/registry/skills/abstractframework-website/references/check_site.py
41
+ src/abstractskill/registry/skills/abstractframework-website/references/preview_server.py
38
42
  src/abstractskill/registry/skills/adr/SKILL.md
39
43
  src/abstractskill/registry/skills/adr/agents/openai.yaml
40
44
  src/abstractskill/registry/skills/adr/references/adr-template-and-workflow.md
@@ -38,8 +38,8 @@ LICENSES = (
38
38
  # The bundle version must move whenever the bundled content moves. When this
39
39
  # test fails: bump `version:` in src/abstractskill/registry/catalog.yaml, then
40
40
  # update BOTH values below (the digest printed in the failure message).
41
- PINNED_VERSION = "2026.10.27"
42
- PINNED_DIGEST = "8e1930494cc8ab8e7ac28c4ccf2b96e6714f72a84f3b71d09b78ce2b2392bb8c"
41
+ PINNED_VERSION = "2026.11.02"
42
+ PINNED_DIGEST = "1486aef6fd33341f74a7d2310a45a3c4c930b40de740fa203b1fcd83935fd687"
43
43
 
44
44
 
45
45
  def _bundle_digest(root: Path) -> str:
@@ -70,11 +70,11 @@ def _all_items() -> set[str]:
70
70
  return skills | set(TRUST_FILES) | set(LICENSES)
71
71
 
72
72
 
73
- def test_bundled_dir_is_the_package_registry_with_15_skills() -> None:
73
+ def test_bundled_dir_is_the_package_registry_with_16_skills() -> None:
74
74
  root = bundled_registry_dir()
75
75
  assert root == REPO / "src" / "abstractskill" / "registry"
76
76
  skills = sorted(p.name for p in (root / "skills").iterdir() if p.is_dir())
77
- assert len(skills) == 15
77
+ assert len(skills) == 16
78
78
  assert all((root / "skills" / name / "SKILL.md").is_file() for name in skills)
79
79
  for name in TRUST_FILES:
80
80
  assert (root / name).is_file()
@@ -26,6 +26,7 @@ GUIDANCE = REPO / "src" / "abstractskill" / "registry" / "guidance.yaml"
26
26
  EXPECTED_SHELF = {
27
27
  "abstract-release",
28
28
  "abstractframework-gateway",
29
+ "abstractframework-website",
29
30
  "adversarial-iteration",
30
31
  "adr",
31
32
  "agora-collaboration",
@@ -49,6 +50,7 @@ EXPECTED_SHELF = {
49
50
  EXPECTED_LEVELS = {
50
51
  "abstract-release": TrustLevel.ADOPTED,
51
52
  "abstractframework-gateway": TrustLevel.FIRST_PARTY,
53
+ "abstractframework-website": TrustLevel.FIRST_PARTY,
52
54
  "adversarial-iteration": TrustLevel.FIRST_PARTY,
53
55
  "agora-collaboration": TrustLevel.FIRST_PARTY,
54
56
  "adr": TrustLevel.ADOPTED,
@@ -66,6 +68,7 @@ EXPECTED_LEVELS = {
66
68
  EXPECTED_SOURCES = {
67
69
  "abstract-release": "codex-skills (maintainer)",
68
70
  "abstractframework-gateway": "first-party",
71
+ "abstractframework-website": "first-party",
69
72
  "adversarial-iteration": "first-party",
70
73
  "agora-collaboration": "first-party",
71
74
  "adr": "codex-skills (maintainer)",
@@ -85,7 +88,9 @@ EXPECTED_SOURCES = {
85
88
  # name HERE — the admission review's explicit signature, never a silent pass.
86
89
  # abstract-release (2026-10-27): a read-only release planner and a registry
87
90
  # visibility gate (stdlib, HTTPS GETs to pypi.org / registry.npmjs.org only).
88
- EXPECTED_SCRIPTS_BEARING: set[str] = {"abstract-release"}
91
+ # abstractframework-website (2026-11-02): two read-only helpers under
92
+ # references/ (a local Range-capable preview server, a static site checker).
93
+ EXPECTED_SCRIPTS_BEARING: set[str] = {"abstract-release", "abstractframework-website"}
89
94
 
90
95
 
91
96
  def test_shelf_discovers_exactly_the_expected_skills() -> None:
File without changes
File without changes