@akinet/akidevrule 3.4.0 → 3.5.0
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.
- package/CHANGELOG.md +22 -0
- package/README.md +15 -11
- package/package.json +1 -1
- package/payload/RULE-agent-behavior.md +1 -0
- package/payload/RULE-content-write.md +3 -3
- package/payload/RULE-docs.md +16 -6
- package/payload/RULE-release.md +35 -15
- package/payload/RULE-seo.md +15 -13
- package/payload/RULE-stack-akiNuxtCf.md +1 -0
- package/payload/index.md +5 -5
- package/skills/aki-article-writer/SKILL.md +7 -7
- package/skills/aki-article-writer/references/article-workflow.md +11 -15
- package/skills/akiflow/scripts/release_lint.py +157 -0
- package/skills/akiopen/SKILL.md +38 -0
- package/skills/akirule/SKILL.md +4 -2
- package/skills/akiship/SKILL.md +3 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [3.5.0] - 2026-09-27
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- **`agent.B3` names the exact git commands that discard or hide tracked/uncommitted work** (`stash`, `checkout -- <path>`/`checkout .`, `restore .`, `reset --hard`, `clean -f[d]`, `push --force`, `branch -D`) as ask-before actions, never a shortcut past a failing check or an obstacle. Evidence: the generic "destructive or hard-to-reverse actions" line already covered this in principle, but stayed unnamed, and this corpus also runs on harnesses (Gemini/Antigravity, Codex, Kiro, Grok) with no built-in git safety protocol to fall back on. `coding.B3`'s narrower stash-for-check-attribution ban is the existing specific instance of this general rule.
|
|
7
|
+
- **`docs.A6` fact docs: `docs/ref/fact-*.md` holds claims about the outside world, and every claim carries its own trail — an official source with the date it was read or an artifact pinned by version with the path inside it, plus the research doc and section that verified it; changes land only through a research event (`## Amendments` or a successor doc) with the `A4` stamp rewritten in the same edit.** Evidence: the owner asked whether facts were held to the same bar as `biz/` decisions, and the corpus answered no — `A2` knew one kind of `ref/` ("verified by running its commands"), `A4` exempted all of `ref/` from the stamp on that reasoning, `B2` linked research → `ref/` one way only, `C3` audited `ref/` for commands alone, and this repo's own four `docs/ref/` lookups are all fact docs by this definition: none stamped, one with an evidence trail. The new `content.C2` fact-check made the gap concrete: it traces every product claim to a source but had nowhere to keep the trace, so every audit re-derives it. Mechanism: one root item naming three claim classes and their authorities — biz decided by the owner (`A3` now says so and why it wins), decision reached by research (`B2`), fact held by evidence (`A6`) — with `A2`, `A4`, `B2` Action, `C3` and `content.C2` pointing at it, and the class declared in the filename so `ls`, the index and a grep all see it. Rejected: requiring evidence on all of `ref/` (a command lookup is verified by running it, a date adds nothing); a frontmatter marker instead of the filename (invisible in a listing); renaming this repo's four existing lookups in this batch (one is deployed by `install.mjs` to a path `tauri.B7` cites, so the rename needs a stale-file prune on users' machines — scheduled in `docs/plan/ref-fact-migration.md`). The "second consumer" bar keeps one-off findings in research, so fact docs do not accumulate trivia.
|
|
8
|
+
- **`seo.A6` URL form: every same-site URL is stored and rendered site-relative; an absolute URL is produced only at the emission boundary, by one idempotent helper whose origin is the configured site URL.** Evidence: a news article on an Aki site rendered its hero `<img>` from `https://<site>/images/...` on localhost, so the dev page showed the production file and would have hidden a missing local asset. Root cause: the corpus taught the pattern, `seo.C1`'s `usePageSeo` example passed `ogImage: 'https://domain.com/...'`, the project's page-creation doc copied it, and every existing post followed it; one field fed both the rendered hero and `og:image`, and nothing separated the two consumers. The article skill already said to store the path relative, but it never said the composable must absolutize it, so following the skill alone would have emitted a relative `og:image`. Mechanism: A6 plus two `seo.C3` detectors (no own-origin `src`/`srcset`/`<a href>` in rendered output; every `og:image` and JSON-LD `image` absolute), `stack.A2` names `site.url` via `useSiteConfig()` as the single origin source, A5/C1 examples switched to relative paths. Rejected: a request-host origin (a preview deploy would emit preview canonical and OG URLs); migrating legacy absolute data (the idempotent helper makes it unnecessary).
|
|
9
|
+
- **`aki-article-writer` Phase 6.5 rendered-output pass: grep the built HTML for own-origin asset URLs, then read the full rendered text in every locale as the target reader before reporting done.** Evidence: the same run passed build and the SEO validator while the page carried the absolute hero URL, keyword variants in parentheses that read unnaturally, and a sentence missing its verb; the owner found all three by looking at the page. Root cause: every Phase 6 check read source strings or validator output, none read what the reader sees. Uses the build, which `coding.B3` self-authorizes, not a dev server.
|
|
10
|
+
- **`akiopen` skill: a session-opening brief that reports only what is still pending in a project.** Evidence: the owner opened every project with the same prompt — check the notes, the active plans, the inbox file, the tree — and the agent rebuilt the picture from memory each time, sometimes missing a surface (`ux.A2`, recognition over recall). The skill reads the five surfaces the corpus already defines (working tree, `.akidevsync/notes.json`, `docs/plan/` outside `done/`, top-level `docs/*.md` inbox files with open checklist items, `[Unreleased]` and plan hand-off lines) in one batched pass and reports at most seven items in a fixed problem / why-still-open / proposal / goal shape, ranked by severity, saying nothing about what is fine. Read-only by construction (`agent.B5`). Rejected after a subtraction pass on the design itself: a detector script (one caller, below `pattern.A2`'s bar — the scan is four shell lines), a SessionStart hook (Claude-only, runs in every directory, and `akiopen` is a deliberate act like `/akiship`, not a passive rule that must fire every turn), and a project-name-specific inbox path (an inbox is any top-level doc with open `- [ ]` items, so no ecosystem name is needed).
|
|
11
|
+
- **`skills/akiflow/scripts/release_lint.py` — mechanical lint for the release record surfaces, wired into `release.C4` and the B7 gate step 4.** Evidence: an audit of 25 project CHANGELOGs found 21 with sections out of order, up to 13 distinct orderings inside one file, 13 invented headings (`Internal`, `Docs`, `Notes`, `Verified`, `Refactored`, `Known`, `Migration`, …), two repos with version headings at H3 so the rule's own `grep '^## \['` state check returned nothing, and 4 of 11 `releases.json` sites with no `highlight` key at all. Root cause: `release.C1` named a vocabulary but no order (and B4 listed a different order), and nothing checked either. Verdict tags `[ORDER]`/`[SECTION]`/`[LEVEL]`/`[PARITY]`/`[TYPE]` fail the gate; `[HILITE]` is a review line (a version with a `new` change and no highlight, more than two highlights, a highlight not first, or a highlight on a `fixed`/`internal` line — the backfill across 22 repos found 8 of those, each on the fix a user would notice most, which is exactly the case C2 excludes because the tier means capability gained, not restored) answered in the receipt, never auto-fixed — whether a change deserves the headline is judgment. Same output grammar and exit codes as `scythe.py`; kept as a separate script because scythe's job is comment/prose format and this is release-record structure (`pattern.A3`). `--latest` scopes it to the newest version so old entries are never a per-release cost. Rejected: folding it into scythe (two responsibilities under one name), and backfilling every repo (historical entries are corrected only when touched; the shape matters going forward). Research: `docs/research/release-changelog-shape-highlight-sep26.md`.
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
- **Publishable prose routes more sensitively.** The `aki-article-writer` description and the router now name the act (writes, rewrites, translates or reviews an article, news/blog post, announcement or knowledge entry, including a release turned into a post) instead of "write a new article"; the router adds a `Publishable writing` line that invokes the skill and loads `content` + `seo`, and the `content` route names articles, posts and content data files. Evidence: the incident request asked for a release to be turned into a news post, which the old trigger ("write a new article, create content, draft a blog post") did not clearly name, and the run then wrote both articles in-thread at the end of a long release session instead of handing them to the Article Worker.
|
|
15
|
+
- **`release.C1` fixes one canonical CHANGELOG shape: `## [x.y.z] - date`, `### Section` one level below, sections at most once, in Keep a Changelog's order `Added, Changed, Deprecated, Removed, Fixed, Security`, no other heading.** Internal/tooling/docs work is `Changed` — no `Internal` section, because `releases.json` already carries the `internal` badge for the audience that needs it and a second taxonomy in the developer channel invites the drift the audit measured. Order is the standard's, not importance-ranked: an order chosen per release cannot be seen across releases, so it never converges. B4's GitHub Release body now says "C1's order" instead of naming its own.
|
|
16
|
+
- **`release.C2` defines the `highlight` tier for `releases.json`.** The field existed in 7 sites' pages and in one page comment on the reference site, nowhere in the rule, so agents wrote flat lists (akitao 0 of 53 versions, app.akinet 0 of 41, the reference site itself 8 versions with a new tool and no highlight). The rule now carries the criterion (the change a returning user came back for; never a fix, internal, or copy tweak), the shape (first line, at most two, zero is valid), the wording contract (benefit-first, mechanism as proof, `title` names the same thing), and the reason: a flat list gives a bug fix and a new tool the same weight, so the public page reads as a log instead of a product moving — the trust signal the page exists to send.
|
|
17
|
+
- **`release.C4` sync check is the script, not two greps.** The greps never checked order, vocabulary, badge keys or highlight, and the H2-only pattern silently passed the two repos whose versions sat at H3.
|
|
18
|
+
- **`release.B6` is now the release-copy contract: one user-facing text per release in three lengths (Headline, Short, Full) plus an announce verdict, printed as the last block of every `/akiship` run.** Before, the only user-facing wording rules were B4's GitHub Release title/body (so a CLI or desktop project with no GitHub Release got none) and three loose B6 lines about dashes and terminology; every other surface — `releases.json` title, a post, a notification, a store listing — was improvised per run. One contract, referenced by B4 (title = `v{version}: {Headline}`, body = Full) and C2 (`title` = Headline), so the wording is written once and read three times (`pattern.A1`). The announce verdict exists because the materiality test applies at the channel too: an internal-only version gets an honest one-liner and `Announce: no`, never a fourth "stability improvements" post. The block quotes what the run already wrote instead of composing a third variant, and never names a channel the project's own records do not.
|
|
19
|
+
- `payload/index.md` release row, `skills/akiship/SKILL.md` gate bullets and report, and `README.md` name the new surfaces.
|
|
20
|
+
- **`content.C2` gains a fourth sweep, fact-check: every claim about a product, feature, version or number must trace to that product's own repo or live page, or it is a finding.** Evidence: a published article on an Aki site stated a capability the product did not have, and the three existing sweeps (canonical-term drift, density, i18n coverage) all passed because none compares a sentence with a source. Root cause: `agent.B2` forbids speculation for the agent's own claims, but the content audit never applied it to shipped copy. Rejected: a new root axiom "write the truth" in `content.A1`, because `agent.B2` already is that rule and a restatement would be the duplication `pattern.A1` forbids. The `content.B3` FAQ bullet is folded into `B2`'s first-sentence rule, which already generalized it; `B2` now carries the FAQ case as its sharpest example instead of pointing at a copy.
|
|
21
|
+
|
|
22
|
+
### Removed
|
|
23
|
+
- **`seo.B3` "Vietnamese keyword handling" and its copies in `aki-article-writer` (§3.4 and the §6.2 checklist line).** The section ordered an unaccented keyword in parentheses at a term's first mention in body copy and FAQ — the direct source of a "(hoan sao)" inserted into a sentence of a published news article. Its premise, that Google treats accented and unaccented Vietnamese as different queries, has no official source in either direction; every line under it inherited that premise, and the alternatives it offered were dead (meta `keywords` is not a ranking signal; `alternateName` variants already live in `seo.B1`). Nothing replaces it: brand-name variants stay in schema via `seo.B1` where only machines read them, and the Phase 6.5 rendered-output pass lists any visible SEO device as a review line. `seo.B4` Prerendering renumbers to `B3`; the repo `CLAUDE.md` Vietnamese-example line no longer cites the removed rule.
|
|
24
|
+
|
|
3
25
|
## [3.4.0] - 2026-09-26
|
|
4
26
|
|
|
5
27
|
### Fixed
|
package/README.md
CHANGED
|
@@ -59,7 +59,7 @@ Interpreter convention (documented once): the installer and hooks run on `node`
|
|
|
59
59
|
|
|
60
60
|
## What you get
|
|
61
61
|
|
|
62
|
-
###
|
|
62
|
+
### Eleven skills
|
|
63
63
|
|
|
64
64
|
| Skill | Invoke | Purpose |
|
|
65
65
|
|---|---|---|
|
|
@@ -69,10 +69,11 @@ Interpreter convention (documented once): the installer and hooks run on `node`
|
|
|
69
69
|
| `akihtmlreport` | `/akihtmlreport` | Distills a dense analysis already in the conversation into one self-contained, ultra-wide `REPORT.html` at the project root — no new analysis, no dropped detail — then opens it locally. Exactly one per project; asks before overwriting. |
|
|
70
70
|
| `akihelp` | `/akihelp` | Live introduction to the whole installed Aki system, rendered by reading `index.md` and skill frontmatters at runtime — it can never go stale. Includes a **painpoint → what to say** table (sprawling CSS, docs that no longer match code, a half-finished tree, a pre-ship check, a hard-to-reverse decision, padded or hard-wrapped output, over-guarded flows, UX friction, pricing calls) built from that live state, with any row whose target is not installed dropped rather than shown. Closes on the caveat that governs everything else: the router is always present but the `Read` of a routed file is still model-dependent, so name the rule file in the prompt whenever the load must be deterministic. |
|
|
71
71
|
| `akigitcommit` | `/akigitcommit` | Turns a messy working tree into a few clean, logically grouped Conventional Commits. Triages a half-finished tree first — finished vs mid-edit vs abandoned vs accidental, asking rather than guessing — then stages by explicit path, never `git add -A`, never pushes unasked. |
|
|
72
|
-
| `aki-article-writer` | `/aki-article-writer` or natural language | Per-project
|
|
72
|
+
| `aki-article-writer` | `/aki-article-writer` or natural language | Per-project pipeline for publishable site prose (articles, news/blog posts, announcements, knowledge entries, including a release turned into a post): research & fact-verification, SEO metadata, JSON-LD schema, UX-psychology-aware content, a rendered-output pass on the built HTML before delivery, and a dedicated Image Scout subagent (Gemini Flash / Haiku) for search → download → visual inspection → ffmpeg processing → slug-named WebP output. One subagent per article; image work is always isolated to a separate lightweight subagent. |
|
|
73
73
|
| `akidevsync-notes` | natural language | Reads/edits a project's `.akidevsync/notes.json` — the per-project task list the Aki-Dev-Sync app itself writes (list/add/pin/mark-done/edit/delete tasks) via a bundled script that preserves the app's own JSON formatting, plus a workflow for cross-checking pinned notes against a shipped release (CHANGELOG + code) before marking them done. |
|
|
74
74
|
| `akilint` | `/akilint` or a penalty card | Mechanical format lint for the penalty-card classes of `RULE-agent-behavior.md` §0: hard-wrapped code comments and markdown prose (`[WRAP]`) and oversize comments (`[YAP]`, always labeled *review* — a flag for judgment against `coding.B4`, never an auto-delete verdict). Thin wrapper over the shared `scythe.py` detector (deterministic line matching, exit-code aware, cannot fabricate evidence) — the same script akiflow's `aki-conduct` seat uses, so a card name means the same thing everywhere. `[FLUFF]` (density) is content judgment and explicitly out of a script's reach. |
|
|
75
|
-
| `
|
|
75
|
+
| `akiopen` | `/akiopen` or natural language | Session-opening brief: reads the working tree, `.akidevsync/notes.json`, active `docs/plan/` files, top-level `docs/*.md` inbox files, `[Unreleased]`, and hand-off lines in one pass, then reports only what is still pending — at most seven items in a fixed problem / why-still-open / proposal / goal shape, ranked by severity. Read-only. |
|
|
76
|
+
| `akiship` | `/akiship` | One-command full release: front-loads every check (release state, tree triage), then runs `RULE-release.md` B7's fail-closed checklist unattended (CRITICAL mandatory `Read` of `RULE-release.md` and `RULE-docs.md` first; a `S0`–`S8` receipt line with quoted evidence per step or the step counts as NOT RUN; written self-interrogation) — diff-scoped hygiene (scythe, dead code, comment doc-refs on the accumulation only), migration doctrine (`RULE-release.md` B5: detector over the diff on every release, startup-embedded migration counts, rehearsal from the PREVIOUS state) and external-action completeness, record truthfulness, build & test mirroring CI (B7 step 6), doc sync across every record surface (plans, `arch`/`feat`, README, task notes, bound standards docs), version mint or defer, registry publish for npm/crates/PyPI packages (`RULE-release.md` B9 — an OTP-gated publish is the single hand-off) — committing via `akigitcommit` with confirmation pre-answered — and ending every run with the release-copy block (`RULE-release.md` B6: headline / short / full plus an announce verdict, quoting the `releases.json` entry and GitHub Release body rather than writing a third text). **Activation is an explicit release order**: the literal token `/akiship`, or an equally explicit imperative naming the ritual for this repo ("release trọn vẹn đi") — a completion word with no release object ("làm cho trọn vẹn"), or `/akiship` inside a question, activates nothing and gets a consult (answer in chat, change nothing). Governed by the B8 contract: that order is the authorization, blockers are reported once as a batch or the run completes with zero mid-run questions, and it stops only for public-history ambiguity, unclassifiable work, or a design contradiction — an owner-worded completion criterion is derived from the anchor plus the repo's own records and decided/reported (`Decided: X · because Y · rejected Z (why) · reopen if W`), escalated only when competing readings would produce different irreversible artifacts. Push/deploy stay opt-in — named explicitly, or via completion-intensity phrasing (canonical list in `RULE-release.md` B8, e.g. "trọn vẹn") — and after any push, CI is watched to green (`RULE-release.md` B10) regardless of whether the stack deploys, and after any deploy a data path the release touched is exercised (`RULE-release.md` B11). |
|
|
76
77
|
|
|
77
78
|
### Five agent definitions
|
|
78
79
|
|
|
@@ -104,7 +105,7 @@ Loading happens on two different mechanisms.
|
|
|
104
105
|
|
|
105
106
|
**Everything else — routed by meaning.** Each task turn is classified by the domains it touches and the act (create, decide, audit, ship), in any language; each route carries concept signals in English and Vietnamese as evidence, never as the test, so a paraphrase routes as well as the listed word. The one model-dependent hop left is the `Read` of a routed file. Sensitivity is deliberately high (err toward loading — a false positive costs a few tokens, a false negative causes wrong behavior).
|
|
106
107
|
|
|
107
|
-
- **Contextual and analytical — read on route match:** `RULE-docs.md` (structure and lifecycle, plus the docs-vs-code drift audit), `RULE-content-write.md` (UI copy and writing style, plus the content audit — canonical-term drift, density deletion test, i18n coverage), `RULE-stack-akiNuxtCf.md`, `RULE-stack-tauri.md` (Tauri v2 + Rust: never-block-the-UI, version SSOT, target context, the macOS TCC/Gatekeeper boundary for spawned sidecars), `RULE-ui-pattern.md` (design-system layer: the subtraction pass that runs before the tier ladder, class taxonomy, tokens, variant API, and the audit playbook), `RULE-seo.md
|
|
108
|
+
- **Contextual and analytical — read on route match:** `RULE-docs.md` (structure and lifecycle, evidence-bound `ref/fact-*` fact docs, plus the docs-vs-code drift audit), `RULE-content-write.md` (UI copy and writing style, plus the content audit — canonical-term drift, density deletion test, i18n coverage, fact-check), `RULE-stack-akiNuxtCf.md`, `RULE-stack-tauri.md` (Tauri v2 + Rust: never-block-the-UI, version SSOT, target context, the macOS TCC/Gatekeeper boundary for spawned sidecars), `RULE-ui-pattern.md` (design-system layer: the subtraction pass that runs before the tier ladder, class taxonomy, tokens, variant API, and the audit playbook), `RULE-seo.md` (metadata, schema, sitemap, and URL form: relative at rest, absolute only where a consumer requires it), `RULE-release.md`, `RULE-db-design.md`, `RULE-biz.md` (market-facing decisions: positioning, pricing, audience) — plus the analytical methods (tagged `Analytical` in `index.md`, loaded on route match like the rest): `METHOD-audit-flow.md` (refactors, multi-file bugs, fragile flows), `METHOD-audit-zero-trust.md` (strict mechanical-first audit: detectors before opinion, exact matches separated from pattern-level candidates), `METHOD-deep-think.md` (scope/architecture/value decisions, first-principles and critique-style thinking), `METHOD-ux-psych.md` (UX/user-behavior evaluation, onboarding and conversion flows), `METHOD-proportionality.md` (sizing a guard, limit or accepted risk against reach, capability, motive and blast radius — the lens that stops both over-engineering and client-side-limits-as-enforcement), `METHOD-audit-subtraction.md` (repo-wide "does this need to exist" sweep, terminating on two dry rounds), and `METHOD-audit-frozen-reference.md` (compliance audit for a clause naming a concrete external artifact as the canonical shape to match — resolve to an exact path, diff literally against it, never judge from memory of the rule's prose).
|
|
108
109
|
- **Full load on explicit request:** asking, in any wording, to load the whole corpus reads every `RULE-*`/`METHOD-*` file at once.
|
|
109
110
|
|
|
110
111
|
No harness magic beyond the `CLAUDE.md` import: routes are instructions telling Claude to Read the file from `~/.aki/akidevrule/` when the task's domain matches; the full-load request is the escape hatch.
|
|
@@ -156,6 +157,7 @@ Install once; from then on the system has two kinds of surface. **Rules load the
|
|
|
156
157
|
| "What is installed here and what do I say to it?" | `/akihelp` |
|
|
157
158
|
| A big, hard-to-reverse, or goal-ambiguous decision | `/akithink` |
|
|
158
159
|
| Work needing several kinds of judgment, or a parallel fan-out | `/akiflow` |
|
|
160
|
+
| Opening a project — what is still pending here? | `/akiopen` |
|
|
159
161
|
| A messy working tree that needs clean commits | `/akigitcommit` |
|
|
160
162
|
| Format lint — or someone called a penalty card | `/akilint` (or just say `[WRAP]` / `[YAP]`) |
|
|
161
163
|
| Ship a release end-to-end | `/akiship` |
|
|
@@ -202,6 +204,7 @@ skills/ → shared Agent Skills corpus (SKILL.md open
|
|
|
202
204
|
akiflow/scripts/council_cost.py (tallies per-agent token usage from the transcript at close-out)
|
|
203
205
|
akiflow/scripts/council_verify.py (mechanical closure gate: ghost seats, missing evidence tags, unanswered REMINDs)
|
|
204
206
|
akiflow/scripts/scythe.py (penalty-card lint [WRAP]/[YAP] — shared engine of /akilint and the enforcer's evidence sweeps)
|
|
207
|
+
akiflow/scripts/release_lint.py (release-record lint: CHANGELOG section order/vocabulary/level, releases.json parity, type keys, highlight review — RULE-release.md C4, B7 step 4)
|
|
205
208
|
akiflow/scripts/*.sh (transitional Unix wrappers, one per script above — each execs its .py sibling)
|
|
206
209
|
akiflow/references/harness-facts.md (subagent/cost/model facts, with sources)
|
|
207
210
|
akithink/SKILL.md
|
|
@@ -209,6 +212,7 @@ skills/ → shared Agent Skills corpus (SKILL.md open
|
|
|
209
212
|
akihelp/SKILL.md
|
|
210
213
|
akigitcommit/SKILL.md
|
|
211
214
|
akilint/SKILL.md
|
|
215
|
+
akiopen/SKILL.md
|
|
212
216
|
akiship/SKILL.md
|
|
213
217
|
aki-article-writer/SKILL.md
|
|
214
218
|
aki-article-writer/references/article-workflow.md
|
|
@@ -250,7 +254,7 @@ flowchart TD
|
|
|
250
254
|
PAYLOAD["payload/ (18 raw rule files)"]
|
|
251
255
|
TCCREF["docs/ref/macos-codesign-tcc.md"]
|
|
252
256
|
PGEMINI["payload/GEMINI.md (template)"]
|
|
253
|
-
CSKILLS["skills/ (
|
|
257
|
+
CSKILLS["skills/ (11 skills, shared open standard)"]
|
|
254
258
|
CCLAUDE["claude/CLAUDE.md (template)"]
|
|
255
259
|
CAGENTS["claude/agents/ (5 agent definitions)"]
|
|
256
260
|
CHOOKS["claude/hooks/aki-update-check.mjs + aki_version_check.mjs (shared parser)"]
|
|
@@ -282,7 +286,7 @@ flowchart TD
|
|
|
282
286
|
G_MD["GEMINI.md (Managed prompt global)"]
|
|
283
287
|
G_LOCAL["GEMINI.local.md (Machine local)"]
|
|
284
288
|
G_RULES["config/rules/akirule-*.md (one per rule file, YAML trigger)"]
|
|
285
|
-
G_SKILLS["config/skills/ (
|
|
289
|
+
G_SKILLS["config/skills/ (11 skills, native auto-discovery)"]
|
|
286
290
|
G_SJSON["config/skills.json (Inherits agskills, absolute path)"]
|
|
287
291
|
end
|
|
288
292
|
|
|
@@ -316,7 +320,7 @@ Targets 4-6 only get the shared skill corpus (no rule corpus / no `CLAUDE.md`/`G
|
|
|
316
320
|
- Before the confirmation prompt or any mutation, preflights every existing JSON file it may update: each detected profile's `settings.json`, `~/.gemini/config/skills.json`, `~/.gemini/antigravity-cli/settings.json`, and `~/.gemini/settings.json`. A malformed file or non-object root aborts the install with originals untouched. After preflight, updates `<target>/settings.json` with a timestamped backup: read permission for `~/.aki/akidevrule/**`, one `Bash(<launcher> <script>*)` rule per Aki skill script per rendering (absolute and `~/`-literal — Claude Code does not expand `~` before matching), `skillOverrides.akirule = "on"`, idempotent registration of the `SessionStart` update-check hook.
|
|
317
321
|
- Installs `<target>/hooks/aki-update-check.mjs` plus its shared parser `<target>/hooks/aki_version_check.mjs`.
|
|
318
322
|
3. Writes `~/.aki/akidevrule/.version` with `installed=`/`version=`/`commit=`/`branch=` and records the source-repo path in `~/.aki/akidevrule/.source-repo` — `version=` is the just-installed CHANGELOG's latest released semver, the same value `install.mjs --check` and the hook compare against remote.
|
|
319
|
-
4. Installs `payload/GEMINI.md` to `~/.gemini/GEMINI.md` — Antigravity global behavior overrides, stamped with a version marker (`[AKIRULE-AG-OVERRIDES-…]`) on line 1. Generates one native rule file per `RULE-*`/`METHOD-*` under `~/.gemini/config/rules/` with YAML `trigger` frontmatter — `agent` `always_on`, the stacks `glob`, the rest `model_decision` — each description generated from its `akirule` route, so both harnesses route from one table. Deploys
|
|
323
|
+
4. Installs `payload/GEMINI.md` to `~/.gemini/GEMINI.md` — Antigravity global behavior overrides, stamped with a version marker (`[AKIRULE-AG-OVERRIDES-…]`) on line 1. Generates one native rule file per `RULE-*`/`METHOD-*` under `~/.gemini/config/rules/` with YAML `trigger` frontmatter — `agent` `always_on`, the stacks `glob`, the rest `model_decision` — each description generated from its `akirule` route, so both harnesses route from one table. Deploys 11 skills directly to `~/.gemini/config/skills/` for native auto-discovery (synced per skill folder, same never-touch-the-rest guarantee as step 2), configures `~/.gemini/config/skills.json` with absolute paths as secondary, and merges skill execution permissions into `~/.gemini/antigravity-cli/settings.json` and `~/.gemini/settings.json` — a `command()` prefix rule for every `skills/*/scripts/*.py` per skill root, in both the expanded and the tilde-literal rendering (agy's matcher compares command strings literally — no glob expansion, and no tilde expansion in either direction — so a directory wildcard never matches and a rule only matches a command written the same way; see [docs/ref/cli-permission-allowlist-standard.md](docs/ref/cli-permission-allowlist-standard.md) §1.2) plus scoped `write_file`/`read_file` rules for the council workspace and rule corpus.
|
|
320
324
|
5. Syncs the same skill folders to `~/.agents/skills/` (Codex CLI, Cursor), `~/.kiro/skills/` (Kiro CLI), and `~/.grok/skills/` (Grok CLI) — each a plain global skills root these CLIs read natively, synced per skill folder name exactly like step 2. Skills-only: no rule corpus is generated for these targets.
|
|
321
325
|
6. Pre-allows every Aki skill script in each harness present on the machine, one adapter per rule dialect (`lib/permissions.mjs`): `~/.kiro/settings/permissions.yaml` (a marker-delimited managed block), `~/.codex/rules/akidevrule.rules` (a file akidevrule owns), `~/.cursor/cli-config.json` (`Shell(python3:<script>*)`, never a bare `Shell(python3)`), `~/.config/opencode/opencode.json` (`permission.bash`). Every rule names one exact script, in both path renderings; entries a previous install wrote are replaced, the user's own are kept. Grok CLI and Ollama have no file-based allowlist, so nothing is written for them — [docs/ref/cli-permission-allowlist-standard.md](docs/ref/cli-permission-allowlist-standard.md).
|
|
322
326
|
|
|
@@ -342,10 +346,10 @@ No sudo, user-local, easy to inspect and delete, consistent with the Aki ecosyst
|
|
|
342
346
|
```bash
|
|
343
347
|
rm -rf ~/.aki/akidevrule
|
|
344
348
|
rm -rf ~/.aki/agent-council # /akiflow session workspaces (self-prunes at 30 days anyway)
|
|
345
|
-
rm -rf ~/.claude/skills/{akirule,akiflow,akithink,akihtmlreport,akihelp,akigitcommit,akilint,akiship,aki-article-writer,akidevsync-notes}
|
|
346
|
-
rm -rf ~/.agents/skills/{akirule,akiflow,akithink,akihtmlreport,akihelp,akigitcommit,akilint,akiship,aki-article-writer,akidevsync-notes} # Codex CLI
|
|
347
|
-
rm -rf ~/.kiro/skills/{akirule,akiflow,akithink,akihtmlreport,akihelp,akigitcommit,akilint,akiship,aki-article-writer,akidevsync-notes} # Kiro CLI
|
|
348
|
-
rm -rf ~/.grok/skills/{akirule,akiflow,akithink,akihtmlreport,akihelp,akigitcommit,akilint,akiship,aki-article-writer,akidevsync-notes} # Grok CLI (other, non-Aki skills already in this folder are untouched)
|
|
349
|
+
rm -rf ~/.claude/skills/{akirule,akiflow,akithink,akihtmlreport,akihelp,akigitcommit,akilint,akiopen,akiship,aki-article-writer,akidevsync-notes}
|
|
350
|
+
rm -rf ~/.agents/skills/{akirule,akiflow,akithink,akihtmlreport,akihelp,akigitcommit,akilint,akiopen,akiship,aki-article-writer,akidevsync-notes} # Codex CLI
|
|
351
|
+
rm -rf ~/.kiro/skills/{akirule,akiflow,akithink,akihtmlreport,akihelp,akigitcommit,akilint,akiopen,akiship,aki-article-writer,akidevsync-notes} # Kiro CLI
|
|
352
|
+
rm -rf ~/.grok/skills/{akirule,akiflow,akithink,akihtmlreport,akihelp,akigitcommit,akilint,akiopen,akiship,aki-article-writer,akidevsync-notes} # Grok CLI (other, non-Aki skills already in this folder are untouched)
|
|
349
353
|
rm -f ~/.claude/agents/aki-{hands,judge,conduct,challenger,maker}.md # your own agents in that folder are untouched
|
|
350
354
|
rm -f ~/.claude/hooks/aki-update-check.mjs ~/.claude/hooks/aki_version_check.mjs ~/.claude/hooks/aki-update-check.py ~/.claude/hooks/aki_version_check.py
|
|
351
355
|
rm -f ~/.gemini/GEMINI.md # restore from a *.akidevrule-backup-* if needed; GEMINI.local.md is left untouched
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akinet/akidevrule",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.0",
|
|
4
4
|
"description": "Aki's shared rule corpus + Agent Skills for Claude Code, Gemini/Antigravity, Codex, Kiro, Grok, Cursor and OpenCode — install and update with one command.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude-code",
|
|
@@ -89,6 +89,7 @@ A worker is a subagent, or the same or another CLI called headlessly (`claude -p
|
|
|
89
89
|
### B3. Decision boundaries
|
|
90
90
|
Ask before:
|
|
91
91
|
- destructive or hard-to-reverse actions — hard-to-reverse means no backup/restore or fix-forward path exists; an action that has one (e.g. an additive migration with a backup, `stack.C8`) climbs `coding.B5`'s ladder instead of asking
|
|
92
|
+
- discarding or hiding tracked/uncommitted work: `git stash`, `checkout -- <path>`/`checkout .`, `restore .`, `reset --hard`, `clean -f[d]`, `push --force`, `branch -D` — run only on the user's explicit ask, never as a shortcut past a failing check or an obstacle (`coding.B3`'s stash-for-attribution ban is the narrow instance of this)
|
|
92
93
|
- changing deployment, infrastructure, auth, billing, or shared config assumptions
|
|
93
94
|
- any test, benchmark, or trial run that spends paid API credits or session quota
|
|
94
95
|
- modifying shared rule files, templates, or project-wide conventions
|
|
@@ -28,7 +28,7 @@ These rules apply to all product content: interface text, meta titles/descriptio
|
|
|
28
28
|
### B2. Writing style — density is enforced, not preferred
|
|
29
29
|
- Prefer clear, concrete wording
|
|
30
30
|
- Deletion test per sentence (domain application of `agent.A4`): a sentence ships only if cutting it loses information the reader needs. Cut preamble, filler connectives, restatement, and reassurance — length follows content, never the reverse.
|
|
31
|
-
- First sentence carries the point (the benefit, the instruction, or the answer); detail follows.
|
|
31
|
+
- First sentence carries the point (the benefit, the instruction, or the answer); detail follows. An FAQ answer is the sharpest case: answer in the first sentence, never a "Đây là...", "According to..." preamble.
|
|
32
32
|
- Avoid filler and vague marketing language unless the project explicitly wants it
|
|
33
33
|
- Keep headings short and literal
|
|
34
34
|
- Punctuation: Strictly limit the use of em dash (—) and en dash (–)
|
|
@@ -38,7 +38,6 @@ These rules apply to all product content: interface text, meta titles/descriptio
|
|
|
38
38
|
- Use stable labels for repeated concepts
|
|
39
39
|
- Avoid unnecessary abbreviations in user-facing text
|
|
40
40
|
- Make important entity definitions obvious near the start of a page or section
|
|
41
|
-
- FAQ answers: answer directly in the first sentence — no "Đây là...", "According to..." preamble
|
|
42
41
|
|
|
43
42
|
## C. Separation
|
|
44
43
|
|
|
@@ -47,8 +46,9 @@ These rules apply to all product content: interface text, meta titles/descriptio
|
|
|
47
46
|
- Do not let temporary task context leak into permanent copy
|
|
48
47
|
|
|
49
48
|
### C2. Content audit
|
|
50
|
-
Read-only (`agent.B5`).
|
|
49
|
+
Read-only (`agent.B5`). Four sweeps, each anchored to the rule it checks:
|
|
51
50
|
1. **Canonical-term drift** (A3) — grep UI strings and i18n keys for synonyms of one concept; one concept with two live labels is a finding.
|
|
52
51
|
2. **Density** (B2) — deletion test per shipped sentence; preamble, restatement, and reassurance in product copy are findings.
|
|
53
52
|
3. **i18n coverage** (A2) — hardcoded user-facing strings that should be keys (excluding the EN=VI exception).
|
|
53
|
+
4. **Fact-check** (`agent.B2`) — every claim about a product, feature, version or number must trace to the project's `ref/fact-*` (`docs.A6`) first, then to that product's own repo or live page; a claim that cannot be traced is a finding, not a style note.
|
|
54
54
|
Classify severity per `docs.C4` (wrong / stale / incomplete / cosmetic); findings spanning domains route into the `docs.C2` research+plan pair.
|
package/payload/RULE-docs.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Core Docs Rules
|
|
2
2
|
|
|
3
|
-
<!-- Address map: docs.A1-
|
|
3
|
+
<!-- Address map: docs.A1-6 · docs.B1-3 · docs.C1-4 -->
|
|
4
4
|
|
|
5
5
|
## Goals
|
|
6
6
|
Docs should be readable for both humans and LLMs.
|
|
@@ -19,7 +19,7 @@ Use these short, stable topic folders:
|
|
|
19
19
|
- `docs/feat/` — features, systems, behaviors
|
|
20
20
|
- `docs/arch/` — architecture, structure, technical design
|
|
21
21
|
- `docs/plan/` — plans and execution notes
|
|
22
|
-
- `docs/ref/` — stable
|
|
22
|
+
- `docs/ref/` — stable lookups: commands, paths and setup steps, verified by running them; `ref/fact-*.md` holds claims about the outside world, verified by evidence (A6)
|
|
23
23
|
- `docs/research/` — exploratory, comparative, or time-bound findings
|
|
24
24
|
|
|
25
25
|
Do not create new top-level doc topics unless the existing set clearly fails.
|
|
@@ -32,10 +32,11 @@ Filenames across all `docs/*` never lead with a date — the content-identifying
|
|
|
32
32
|
- For any project with a business dimension, `docs/biz/` is REQUIRED and is the spine.
|
|
33
33
|
- All `arch/`, `feat/`, and `plan/` docs that touch product direction or money must reference it.
|
|
34
34
|
- When code intent and a `biz/` doc disagree, the `biz/` doc wins — reconcile or escalate.
|
|
35
|
+
- `biz/` holds decisions, not facts: it wins because the owner chose it, never because a source proved it. A market fact the decision rests on (a competitor's price, a platform limit) belongs in `ref/fact-*` with its evidence (A6), and the `biz/` doc cites it.
|
|
35
36
|
|
|
36
|
-
### A4. Anchor stamp — `updated <time> <version>` on every `arch|biz|feat` doc
|
|
37
|
+
### A4. Anchor stamp — `updated <time> <version>` on every `arch|biz|feat` doc and every `ref/fact-*`
|
|
37
38
|
|
|
38
|
-
`arch/`, `biz/` and `feat/` hold current state and are the SSoT other docs and code are written against, so a reader cannot tell a still-true doc from a silently rotted one without knowing when it was last confirmed. These three folders carry a stamp
|
|
39
|
+
`arch/`, `biz/` and `feat/` hold current state and are the SSoT other docs and code are written against, so a reader cannot tell a still-true doc from a silently rotted one without knowing when it was last confirmed. These three folders carry a stamp, and so does every `ref/fact-*` doc (A6): a fact is confirmed by reading a source on a date, and the outside world moves without touching this repo. `plan/`, `research/` and the rest of `ref/` do not — the first two are event records whose own schema already dates them (B1, B2), and a command lookup is verified by running it, not by a date.
|
|
39
40
|
|
|
40
41
|
**Placement** — first line of the file's own header block: immediately under the H1 for a plain Markdown doc, or as a `updated:` key in the frontmatter/description field where the file already has one. One stamp per file, never per section.
|
|
41
42
|
|
|
@@ -62,6 +63,15 @@ The harness prepends this file to every request, so every line in it is paid on
|
|
|
62
63
|
|
|
63
64
|
One file is the source: a per-project `GEMINI.md` or `AGENTS.md` is a bootstrap that points at `CLAUDE.md`, never a second copy.
|
|
64
65
|
|
|
66
|
+
### A6. Fact docs — `docs/ref/fact-*.md`
|
|
67
|
+
|
|
68
|
+
Three claim classes, three authorities, never interchangeable: a **biz** claim is decided by the owner and wins by decision (A3); a **decision** is reached by research and holds by its recorded reasoning (B2); a **fact** is a claim about the outside world — a vendor's behavior, a limit, a price, what a standard specifies, what an artifact of a given version contains — and holds only by evidence. A fact nobody can trace is an unverified claim: it lives in `research/` marked as such, never in `ref/fact-*`.
|
|
69
|
+
|
|
70
|
+
- **Every fact carries its own trail, on the claim, not only on the file**: the source — an official page with the date it was read, or a named artifact pinned by version with the path inside it — and the research doc and section that verified it (`research/<doc>.md § R3`). A fact with a source but no research origin was asserted, not verified.
|
|
71
|
+
- **Current state only; history lives in research.** The fact doc is the distilled answer (B2 Action). Every change lands through a research event — an `## Amendments` entry when the finding stands, a successor doc when it changes — and the fact doc is updated in the same edit with its stamp rewritten (A4). A fact doc edited with no research event behind it is a **Wrong** finding (C3).
|
|
72
|
+
- **A fact earns its row when a second consumer needs to cite it** — a rule, another doc, an audit (`content.C2`'s fact-check reads here before the product's repo or live page). A one-off finding stays in the research doc that produced it.
|
|
73
|
+
- **The filename declares the class**: `fact-<subject>.md`. The rest of `ref/` stays commands and setup, verified by running.
|
|
74
|
+
|
|
65
75
|
## B. Lifecycle & Sync
|
|
66
76
|
|
|
67
77
|
### B1. Plan lifecycle & Filename Rules
|
|
@@ -90,7 +100,7 @@ Required fields, in order:
|
|
|
90
100
|
- **Verification** — the evidence/method that hardens the result (data, test, cross-check against another case). If not verified, say so explicitly — silence reads as certainty when it isn't.
|
|
91
101
|
- **Corroborating links** — links to the evidence/cases the result rests on or conflicts with (not just a verified/unverified flag)
|
|
92
102
|
6. **Decision** — the resolution reached, one of:
|
|
93
|
-
- **Action** — link to the artifact(s) where it materialized (`arch/`, `plan/`, `feat/`, `biz/`, `ref/`, or code/commit); 0 or many. Landing in `ref/` always means a **new** clean lookup doc, never the research doc itself relocated or rewritten into ref format — `ref/` is a distilled answer, research is the narrative trail behind it.
|
|
103
|
+
- **Action** — link to the artifact(s) where it materialized (`arch/`, `plan/`, `feat/`, `biz/`, `ref/`, or code/commit); 0 or many. Landing in `ref/` always means a **new** clean lookup doc, never the research doc itself relocated or rewritten into ref format — `ref/` is a distilled answer, research is the narrative trail behind it. A fact lands in `ref/fact-*` (A6) with this doc's section named on the claim, so the trail runs both ways.
|
|
94
104
|
- **No action** — state why explicitly, so it reads as a deliberate stop, not an abandoned doc
|
|
95
105
|
- **Follow-up research** — link to the new research doc opened by this result
|
|
96
106
|
- **Rejected/closed** — an option eliminated with no replacement; no link needed
|
|
@@ -139,7 +149,7 @@ Walk the topology, checking each doc against what is actually true now:
|
|
|
139
149
|
- `docs/feat/` — the described behavior still matches what the code does
|
|
140
150
|
- `docs/biz/` — where code intent contradicts it, A3 decides: the `biz/` doc wins until it is explicitly changed
|
|
141
151
|
- `docs/research/` — a conclusion whose recorded context no longer holds needs a successor doc plus a `Status: superseded by` line; a claim corrected while the Decision stands needs an `## Amendments` entry plus a `Status: amended` notice; a body rewritten in place with neither is a **Wrong** finding (B2)
|
|
142
|
-
- `docs/ref/` — commands, paths, and setup steps still run
|
|
152
|
+
- `docs/ref/` — commands, paths, and setup steps still run; in `ref/fact-*`, every source still resolves and still says what the claim says, every pinned artifact version is still the one in use, and every claim's research origin exists — a claim changed with no research event behind it is **Wrong** (A6)
|
|
143
153
|
- Doc references inside code comments (B3) still point at a heading that exists
|
|
144
154
|
- **The inverse walk — code → docs:** a complex feature or subsystem shipped with no corresponding `feat/`/`arch/` doc is an **Incomplete** finding (C4). The audit checks both directions, never only whether existing docs still hold
|
|
145
155
|
|
package/payload/RULE-release.md
CHANGED
|
@@ -106,11 +106,7 @@ After updating CHANGELOG and the version bump, produce the GitHub Release withou
|
|
|
106
106
|
- **Otherwise** (no `gh`, or the user will publish manually) → output the copy-ready block below instead.
|
|
107
107
|
- Before minting, cross-check tags against Releases (`gh release list` vs `git tag`) and offer to backfill any tag that has no matching Release, so the Releases page has no gaps.
|
|
108
108
|
|
|
109
|
-
**Title:** `v{version}: {
|
|
110
|
-
- Good: `v1.5.1: fix production icons blank, caret, grid gap`
|
|
111
|
-
- Bad: `v1.5.1: patch fixes`, `v1.5.1: various improvements`
|
|
112
|
-
|
|
113
|
-
**Body:** same `#### Fixed` / `#### Changed` / `#### Added` sections as CHANGELOG, but each bullet trimmed to one short sentence — symptom first, no file paths, no internal jargon.
|
|
109
|
+
**Title and body are B6's tiers, written once:** title `v{version}: {Headline}`, body = the Full tier. Nothing about their wording lives here.
|
|
114
110
|
|
|
115
111
|
**Compare link (GitHub-hosted repos — mandatory footer):** the notes end with `**Full Changelog**: <repo-url>/compare/<prev-tag>...<new-tag>` — or use `gh release create --generate-notes`, which inserts it automatically. The Release page renders notes only, never a diff, and the tag itself points at a single commit (usually the version-mint commit, a tiny diff) — without this line there is no one-click view of the commits accumulated since the previous release. First release with no prior tag: link `<repo-url>/commits/<new-tag>` instead.
|
|
116
112
|
|
|
@@ -133,10 +129,32 @@ Do not report a plan, task, or release/deploy as complete when a migration/infra
|
|
|
133
129
|
4. **REHEARSE from the PREVIOUS state, never from empty.** A test or dry-run that starts from a fresh database exercises `CREATE`, not the migration, and proves NOTHING about an upgrade. REQUIRED evidence: the migration executed against (a) a schema generated or snapshotted from the previous release, AND (b) for any data-dependent change (unique index, `NOT NULL` backfill, type change, dedupe) a COPY of real target data. State which was run and quote its output. "The tests pass" is not evidence.
|
|
134
130
|
5. **POSTCONDITIONS asserted, ROLLBACK named.** After the real run, assert expected columns, indexes and row counts by query (condition 1 above), and record the backup or fix-forward path BEFORE any destructive step ([[RULE-agent-behavior]] B3). A migration with no stated rollback or fix-forward path does NOT ship.
|
|
135
131
|
|
|
136
|
-
### B6.
|
|
137
|
-
|
|
138
|
-
-
|
|
139
|
-
-
|
|
132
|
+
### B6. Release copy — one user-facing text per release, three lengths, printed by default
|
|
133
|
+
The developer record and the user-facing copy are two channels (C1); this item owns the user-facing one for every project type, and every surface that shows a release to a person renders it: the GitHub Release (B4), `releases.json` (C2, web only), an in-app "what's new", a store listing, a post or notification the owner sends by hand. One source, three lengths, all telling one story (`biz.C4`):
|
|
134
|
+
- **Headline** — at most 12 words naming the one thing the user gets: the C2 highlight when there is one, else the most user-visible change. It is the GitHub Release title after `v{version}: ` (A3: the `v` is render-time only) and the `releases.json` `title`. Good: `fix production icons blank, caret, grid gap`; bad: `patch fixes`, `various improvements`, `bug fixes`.
|
|
135
|
+
- **Short** — one or two sentences for a post, a notification, a chat message: the headline's benefit, the concrete mechanism as proof, the link. Says nothing the Full tier does not.
|
|
136
|
+
- **Full** — the highlight first as its own line, then every other change as one sentence each, grouped under C1's sections in C1's order, symptom first for fixes; the compare-link footer per B4 when GitHub-hosted.
|
|
137
|
+
Wording in every tier: benefit first, then proof (`biz.C1`); what the user can now do, never the file, route, symbol or component; no em/en dash, short sentences (`content.B2`); the audience's language per C1's table (English default, plus Vietnamese where the product is bilingual); one canonical term per concept across versions ("Release Notes", never a synonym — `content.A3`). Internal-only work is one honest line ("under-the-hood improvements…", C3), never dressed as a feature.
|
|
138
|
+
**Choosing the headline — a protocol with a written verdict, never a feeling.** The agent that wrote the code is the worst judge of what users gained: it ranks by effort spent, by what landed last, or by what the owner talked about most, and none of those is value. So the choice is made from the user's side, in five steps, and the reasoning is written into the run's receipt where the owner can overrule it:
|
|
139
|
+
1. **Candidates** — every change in the accumulation that alters what a user can do: a new tool, page, mode, format, integration, or an option inside one. `fixed` and `internal` are excluded by construction; a fix that unblocks a core flow may become the *Headline* when nothing else qualifies, but it is never a `highlight` (C2), because the highlight tier means capability gained, not capability restored.
|
|
140
|
+
2. **Three kill-tests per candidate, from the primary audience's seat** (`docs/biz/`, `biz.A1`; when no audience is recorded, the person the product's front page addresses). *Return:* would someone who last used the product before this version come back, or use it differently, because of this? *Tell:* can it be said in one sentence that person would repeat to a peer, with a concrete verb and no file, route, component or internal term? *Before/after:* is there something they could not do before, or could only do with a workaround? A polish, copy, layout or speed change fails the third unless it removes a workaround; an admin-only or owner-only capability fails the first; a candidate whose Tell sentence needs internal vocabulary fails the second. One failed test disqualifies.
|
|
141
|
+
3. **Rank survivors by reach × delta**, both estimated and labeled so: reach is how many of that audience meet it in a normal session (every session > a common task > a niche path); delta is the size of the gain (a new tool or mode > a new option inside an existing tool > a removed workaround).
|
|
142
|
+
4. **Cut to one.** A second only when it is independent of the first (a different job, not a sub-feature of it) and ranks close; three never — a third means the release bundled two releases (A5 materiality) or the ranking is undecided, and undecided resolves to one, not two. Zero survivors is a normal result: the Headline then names the most user-visible fix or change, and the announce verdict is judged on that.
|
|
143
|
+
5. **Cross-check the surfaces before writing.** The Headline, the first `changes[]` line, the `highlight` flag, the Short tier and the GitHub Release title must all point at the same thing; when the title you would naturally write names something the highlight does not, the selection is wrong, not the title. Then write the verdict into the receipt: one line per candidate — `highlight: <thing> — passes return/tell/before-after, reach every session, delta new mode` or `rejected: <thing> — fails return (admin-only)` — so a `release_lint.py` `[HILITE]` line is answered by this record and the owner can overrule a choice without re-deriving it.
|
|
144
|
+
|
|
145
|
+
**Announce verdict.** The copy ends with `Announce: yes` when the version carries a highlight or a fix the user would notice, or `Announce: no — <reason>` for an internal-only or invisible-fix accumulation: a follower who reads three "stability improvements" posts in a week stops reading the fourth, so the materiality test (A5) applies at the channel too. Channels are the project's own, read from `docs/biz/`, the project `CLAUDE.md` or an existing post history — never invented; with none recorded the verdict stands alone.
|
|
146
|
+
**Where it is produced.** Every `/akiship` run ends with this block, a deferred version included (`deferred — no copy`), and any other run that mints a version prints it in its closing report. It is reused, never rewritten: the `releases.json` entry and the GitHub Release body already are this copy, so the block quotes them rather than composing a third variant; where neither exists (CLI, desktop app without a GitHub Release) the block is the copy's only home and the owner pastes it where it goes.
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
## Release copy — {version} ({date})
|
|
150
|
+
Headline: …
|
|
151
|
+
Short: …
|
|
152
|
+
Full:
|
|
153
|
+
…
|
|
154
|
+
Announce: yes | no — <reason>
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Doc/version moves are part of the change, not an afterthought ([[RULE-docs]]).
|
|
140
158
|
|
|
141
159
|
### B7. Pre-ship gate — work finished, nothing pushed yet
|
|
142
160
|
|
|
@@ -154,7 +172,7 @@ Run in order; each step names the rule that owns it.
|
|
|
154
172
|
1. **Release state** — derive it cold from the repo per B1, never from session memory. `Drifted` blocks everything until A5's recovery has run.
|
|
155
173
|
2. **Hygiene sweep — scoped to the accumulation, never the whole repo.** On the files touched since the boundary commit (B1.5): scythe `[WRAP]`/`[YAP]` lint ([[RULE-agent-behavior]] §0), dead code / redundant guards / duplication the accumulation itself introduced (`pattern.A8`; subtract-class detectors at diff scope), and doc references in touched comments still resolving ([[RULE-docs]] B3). A repo-wide subtraction or zero-trust sweep is a separately scheduled audit, never a per-release cost — diff scope is what keeps this gate affordable at many releases per day. Unlike an audit, findings here are fixed in place: this is a gate, not a report.
|
|
156
174
|
3. **Migration & external-action completeness — the B5 detector runs FIRST, on EVERY release, without exception.** Paste its output (or `empty`) into the receipt. A hit obliges a written answer to each of B5 points 2–5: is it separate, is the order expand → migrate → deploy → contract, was it rehearsed from the PREVIOUS state (which one, quoted output), are postconditions and rollback stated. Startup-embedded migration code counts. Then every other change whose "done" lives outside the repo (remote config, env vars, cron registrations, cache purges) is confirmed live, and each script sits in its completion location ([[RULE-coding]] B3). A green build proves nothing about the database; a green test on an empty database proves nothing about an upgrade.
|
|
157
|
-
4. **Record truthfulness** — every closed problem has its `CHANGELOG.md` entry, and no entry claims something step 3 has not cleared (B2). Web stacks additionally need `releases.json` parity (C3).
|
|
175
|
+
4. **Record truthfulness** — every closed problem has its `CHANGELOG.md` entry, and no entry claims something step 3 has not cleared (B2). Web stacks additionally need `releases.json` parity (C3). Shape is mechanical: `python3 ~/.claude/skills/akiflow/scripts/release_lint.py --latest .` (C4) must exit 0; a `[HILITE]` review line is answered in writing per C2, never silently passed.
|
|
158
176
|
5. **Doc sync — every record surface the accumulation touched, not only `docs/`.** Enumerate, then check each against the diff: plans whose work shipped moved to `docs/plan/done/`; `arch`/`feat` docs match what is about to ship ([[RULE-docs]] B1, B3); `README.md` wherever the accumulation changed setup, commands, layout, or a documented behavior; the project's task-note file when one exists (`.akidevsync/notes.json`, edited only through the `akidevsync-notes` skill — a note whose fix is in this accumulation is marked done with the matching CHANGELOG line, an unmatched or unverified one stays open and is named in the report); and any external standards doc the project `CLAUDE.md` binds the project to, updated in place when the accumulation changed a convention that doc owns. A surface skipped because it was not in `docs/` is the same drift finding as a stale doc.
|
|
159
177
|
6. **Build & test — mirror CI.** Commands are derived, never invented: the jobs `.github/workflows/*` run on push/tag take priority; a repo with no such workflow falls back to the manifest's own scripts (`npm run typecheck`/`build`/`test`, `cargo build`/`cargo test`, equivalent). Run every one of them locally, self-authorized ([[RULE-coding]] B3 — ship/release is the moment full build+test is mandatory, not optional). A failure blocks the gate and is fixed in place, same as step 2. A CI step that cannot be reproduced locally (an other-OS matrix leg, a job needing secrets) is named explicitly and left to B10 to catch post-push. A repo with no build/test command at all says so plainly — that is a finding, not a silent pass. This step sits after 2–5 because those fix code and docs first, and the build must cover what is actually about to ship.
|
|
160
178
|
7. **Verification honesty** — anything only checkable at runtime is reported as unverified rather than assumed ([[RULE-coding]] B3). "Untested but I expect it works" is a valid gate output; a silent "Done" is not.
|
|
@@ -204,17 +222,20 @@ After ANY deploy or restart, in any flow (not only `/akiship`), verify FUNCTION,
|
|
|
204
222
|
### C1. Two separate channels — do not merge them
|
|
205
223
|
| File | Audience | Language | Tone |
|
|
206
224
|
|------|----------|----------|------|
|
|
207
|
-
| `CHANGELOG.md` | developer / technical | English only | Precise, may name files/symbols. Keep a Changelog
|
|
225
|
+
| `CHANGELOG.md` | developer / technical | English only | Precise, may name files/symbols. Keep a Changelog shape, closed and ordered — see below |
|
|
208
226
|
| `app/data/releases.json` | public / end user | Bilingual EN + VI if the site is multilingual (default EN); EN-only if single-language | Popular, user-friendly, benefit-first. No jargon, no file paths |
|
|
209
227
|
|
|
210
228
|
The changelog explains *what changed and why* for maintainers. The release note tells users *what they get*. Write them separately; do not paste changelog lines into the release note.
|
|
211
229
|
|
|
230
|
+
**CHANGELOG shape — one canonical form, mechanically checked (`release_lint.py`, C4).** Version heading `## [x.y.z] - YYYY-MM-DD` (`## [Unreleased]` while open), sections `### <Name>` at exactly one level below, each at most once per version, in this fixed order and from this closed vocabulary: `Added`, `Changed`, `Deprecated`, `Removed`, `Fixed`, `Security`. Sections that ship nothing are omitted, never left empty. There is no `Internal`, `Docs`, `Notes`, `Verified`, `Refactored` or any other heading: internal, tooling and docs work is `Changed`; a verification or caveat is a clause on the bullet it qualifies; a decision's reasoning lives in the bullet or in `docs/research/`. The order is the standard's, not importance-ranked: an order chosen per release is unobservable across releases and drifts the moment two people, or two sessions, write entries. Evidence: 21 of 25 audited projects had entries out of order, 9 distinct orderings within one file, 13 invented headings across the set, and this rule itself named two different orders in two sections.
|
|
231
|
+
|
|
212
232
|
`releases.json` exists **only where a public web page renders it** (the Nuxt stack's release-notes page). Tauri, CLI, and other non-web projects keep `CHANGELOG.md` only — a release-notes file nothing renders is dead data; do not create one. Where `releases.json` does not exist, every rule below that mentions it simply does not apply.
|
|
213
233
|
|
|
214
234
|
### C2. releases.json schema
|
|
215
235
|
- Single-language site: `{ version, date, title, changes: [{ type, text }] }`
|
|
216
236
|
- Multilingual site: localize the human text — `title: { en, vi }`, `changes: [{ type, text: { en, vi } }]`. Keep `version`, `date`, `type` locale-neutral. Default/fallback language is English.
|
|
217
237
|
- `type` is one of `new` | `improved` | `fixed` | `internal` (stable badge keys).
|
|
238
|
+
- `highlight: true` (optional, locale-neutral) marks the line B6's headline protocol selected — a capability gained, chosen from the primary audience's seat through the three kill-tests and the reach × delta ranking, with the verdict written in the run's receipt. It is a tier, never a filter: every change still ships as a line (C3); the page renders the highlighted line as a distinct card, because a flat list gives a bug fix and a new tool the same weight, so the reader's eye has nothing to land on and the page reads as a log, not a product moving. Shape: the highlighted line is the **first** in `changes[]`; at most one, two only when B6 step 4 allows it; never on `fixed` or `internal`; zero is correct when no candidate survives. The `title` is B6's Headline tier and names the same thing the highlight marks (one story per surface, `biz.C4`); a title that headlines something the highlight does not, or vice versa, means one of them is wrong. Write the highlighted line benefit-first with the concrete mechanism as proof (`biz.C1`, `content.B2`): what the user can now do, then how — never the file, route or component that does it. Mechanical review: `release_lint.py` emits `[HILITE]` for a version with a `new` change and no highlight — a candidate, answered by B6 step 5's per-candidate verdict lines in the gate receipt, never silently passed.
|
|
218
239
|
|
|
219
240
|
### C3. No version gaps, and no content gaps, in releases.json
|
|
220
241
|
Every version that appears in `CHANGELOG.md` MUST also appear in `releases.json` (no missing version), and every `Added`/`Changed`/`Fixed`/`Removed` section in that version's CHANGELOG entry must be represented by at least one `changes[]` line in `releases.json` (no missing content) — skipping a version, or silently dropping a whole category of its work, because it reads as "internal" or "technical" is not allowed. This page is the one place both a human visitor and a crawling/LLM bot judge whether the product is actively maintained; a version that reads as empty is worse than one that reads as unglamorous.
|
|
@@ -228,14 +249,13 @@ Every version that appears in `CHANGELOG.md` MUST also appear in `releases.json`
|
|
|
228
249
|
Never leave a gap like `1.0.5 → 1.0.7` or `0.1.0 → 0.1.3` in releases.json. A one-line entry is better than a missing version.
|
|
229
250
|
|
|
230
251
|
### C4. Sync check — required before closing a task
|
|
231
|
-
After editing `CHANGELOG.md` or `releases.json`, run:
|
|
252
|
+
After editing `CHANGELOG.md` or `releases.json`, run from the project root:
|
|
232
253
|
|
|
233
254
|
```
|
|
234
|
-
|
|
235
|
-
grep -E '^## \[' CHANGELOG.md
|
|
255
|
+
python3 ~/.claude/skills/akiflow/scripts/release_lint.py --latest .
|
|
236
256
|
```
|
|
237
257
|
|
|
238
|
-
|
|
258
|
+
Exit 0 is the pass. Verdict tags — `[ORDER]`, `[SECTION]`, `[LEVEL]` (C1 shape), `[PARITY]` (a version in one surface and not the other, C3), `[TYPE]` (a badge key outside C2) — are fixed before the task closes. `[HILITE]` is a review line (C2), answered, never auto-fixed. Without `--latest` the script sweeps every version — that is an audit run (`agent.B5`), never a per-task cost; historical entries are corrected only when a task already touches them, never as a backfill sweep.
|
|
239
259
|
|
|
240
260
|
### C5. Live production verification
|
|
241
261
|
Never trust a deployment CLI's success status alone. A web deploy is only verified when the live production URL explicitly returns the new version data.
|
package/payload/RULE-seo.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# SEO Rule — Nuxt + Cloudflare Stack
|
|
2
2
|
|
|
3
|
-
<!-- Address map: seo.A1-
|
|
3
|
+
<!-- Address map: seo.A1-6 · seo.B1-3 · seo.C1-3 (⟨Aki⟩) -->
|
|
4
4
|
|
|
5
5
|
## Scope
|
|
6
6
|
Cross-project rules for all Nuxt 4 + Cloudflare Pages sites. For project-specific keyword strategy and schema values, see the project's own `docs/ref/seo.md` or equivalent.
|
|
@@ -78,6 +78,15 @@ sitemap: {
|
|
|
78
78
|
- **Location**: `public/ogimage/[slug].jpg` or `.png`
|
|
79
79
|
- **Fallback**: if page-level image is absent, the composable falls back to the site-wide default OG image
|
|
80
80
|
- Never reference an OG image path that doesn't exist in `public/`
|
|
81
|
+
- Referenced site-relative (`/ogimage/slug.jpg`); the SEO composable makes it absolute (A6)
|
|
82
|
+
|
|
83
|
+
### A6. URL form — relative at rest, absolute only at emission
|
|
84
|
+
|
|
85
|
+
- Every same-site URL is stored and rendered root-relative (`/images/x.jpg`, `/path/`): content data, internal links, `<img src>`/`srcset`, asset references. An own-origin literal (`https://domain.com/...`) there breaks localhost and preview deploys, and hides a missing local asset behind the production copy.
|
|
86
|
+
- Consumers that require an absolute URL (`og:image`, `twitter:image`, `og:url`, canonical, hreflang, JSON-LD `url`/`image`/`logo`, sitemap, RSS, email, share text) get it from one helper at the emission boundary (SEO composable, schema builder, feed generator), never from the stored value. The helper is idempotent (relative → absolute, absolute → unchanged), so legacy absolute data keeps working without a migration.
|
|
87
|
+
- The origin comes from the one configured site URL (`pattern.A1`), never from the request host: a preview deploy must still emit production canonical and OG URLs.
|
|
88
|
+
- A subpath deploy (`baseURL` ≠ `/`) prefixes through the framework's base-URL mechanism, never by string concatenation per call site.
|
|
89
|
+
- Detected mechanically in C3.
|
|
81
90
|
|
|
82
91
|
## B. AI visibility & entity
|
|
83
92
|
|
|
@@ -91,7 +100,7 @@ These rules help content appear in AI-generated answers (Perplexity, ChatGPT, Ge
|
|
|
91
100
|
- **alternateName in Organization/WebSite schema**: include all brand spelling variants (accented + unaccented + lowercase + domain form) so AI can resolve them to a single entity
|
|
92
101
|
- **knowsAbout**: list the topics the brand covers — helps AI cite the site as a relevant source
|
|
93
102
|
|
|
94
|
-
> ⚠️ **`llms.txt` is not an AI-visibility strategy (2026).** A log study across 137,000 domains found **97% of `llms.txt` files received zero requests over a full month**; no major LLM vendor has committed to reading the format, and Google's John Mueller has compared it to the meta keywords tag — a standard proposed by publishers that no consumer agreed to honour. Keep the file if it already exists (it costs nothing and is genuinely useful for *internal* agents reading the site), but never list it as an SEO/GEO deliverable and never let it substitute for the thing that does work: getting the content into the server-rendered HTML (
|
|
103
|
+
> ⚠️ **`llms.txt` is not an AI-visibility strategy (2026).** A log study across 137,000 domains found **97% of `llms.txt` files received zero requests over a full month**; no major LLM vendor has committed to reading the format, and Google's John Mueller has compared it to the meta keywords tag — a standard proposed by publishers that no consumer agreed to honour. Keep the file if it already exists (it costs nothing and is genuinely useful for *internal* agents reading the site), but never list it as an SEO/GEO deliverable and never let it substitute for the thing that does work: getting the content into the server-rendered HTML (B3).
|
|
95
104
|
|
|
96
105
|
### B2. Entity & ecosystem linking
|
|
97
106
|
|
|
@@ -103,16 +112,7 @@ For sites that belong to a multi-site ecosystem or brand family:
|
|
|
103
112
|
|
|
104
113
|
Keep the concrete domain list (parent org URL, sibling sites) in the project's own docs — one source of truth per ecosystem, not hardcoded in shared rules.
|
|
105
114
|
|
|
106
|
-
### B3.
|
|
107
|
-
|
|
108
|
-
Google treats accented and unaccented Vietnamese as different queries (`vst là gì` ≠ `vst la gi`). To cover both without degrading UX:
|
|
109
|
-
|
|
110
|
-
- **Embed the unaccented form in parentheses** in the first mention of a term in body copy or FAQ: *"...VST (vst la gi)..."*
|
|
111
|
-
- **Or include it in** `keywords` meta or `alternateName` in schema
|
|
112
|
-
- **Never** put unaccented forms in H1, H2, visible headings, or the FAQ question text — it looks unprofessional
|
|
113
|
-
- **Meta title and description**: use correctly accented Vietnamese; unaccented coverage comes from schema + body copy
|
|
114
|
-
|
|
115
|
-
### B4. Prerendering & SSR
|
|
115
|
+
### B3. Prerendering & SSR
|
|
116
116
|
|
|
117
117
|
- SEO-critical content must be in the HTML at crawl time — not injected by client-side JS. **This is the single highest-evidence rule in this file for AI visibility**: roughly 69% of AI crawlers (GPTBot, OAI-SearchBot, ClaudeBot, Claude-SearchBot, PerplexityBot) do **not** execute JavaScript, so a client-only SPA is simply invisible to them. Googlebot and Gemini do render; ChatGPT, Claude and Perplexity do not. Prerendering beats every schema tweak combined.
|
|
118
118
|
- Public pages: prerender/SSG preferred
|
|
@@ -129,7 +129,7 @@ Every public page must call `usePageSeo()`. Canonical URL is derived automatical
|
|
|
129
129
|
usePageSeo({
|
|
130
130
|
title: 'Page Topic', // Max 60 chars total — NO brand suffix (see @nuxtjs/seo note below)
|
|
131
131
|
description: 'Action-oriented…', // Max 155 chars, unique per page
|
|
132
|
-
ogImage: '
|
|
132
|
+
ogImage: '/ogimage/slug.jpg', // optional, site-relative — the composable absolutizes it (A6)
|
|
133
133
|
ogImageAlt: 'Description of image', // optional
|
|
134
134
|
noindex: true, // optional, for admin/private pages
|
|
135
135
|
})
|
|
@@ -161,6 +161,8 @@ Run `scripts/validate-seo.js` (or equivalent) after every build. At minimum it s
|
|
|
161
161
|
- [ ] All descriptions ≤ 155 chars
|
|
162
162
|
- [ ] No em dash (`—`) or en dash (`–`) in title or description
|
|
163
163
|
- [ ] All canonical URLs end with `/`
|
|
164
|
+
- [ ] No rendered `src`, `srcset` or `<a href>` carries the site's own origin (A6); `<head>` `link`/`meta` are exempt, they are emission targets
|
|
165
|
+
- [ ] Every `og:image`, `twitter:image` and JSON-LD `image` is an absolute `https://` URL
|
|
164
166
|
- [ ] Homepage `Organization` schema has `alternateName` and `sameAs`
|
|
165
167
|
- [ ] `/admin/**` pages absent from sitemap output
|
|
166
168
|
- [ ] Skip redirect stub files (`http-equiv="refresh"`) — they have no SEO content to validate
|
|
@@ -27,6 +27,7 @@ Nuxt 4 · Vue 3 · Tailwind v4 · @nuxtjs/i18n · @nuxtjs/seo · SweetAlert2 ·
|
|
|
27
27
|
- `crypto.subtle` operates on bytes — feed it `new TextEncoder().encode(str)`, not the string.
|
|
28
28
|
- Do not enable `nodejs_compat` in `wrangler.toml` unless upstream issues are confirmed fixed
|
|
29
29
|
- Trailing slash: `trailingSlash: true` everywhere (routing, canonical, og:url, sitemap, schema.org) — canonical config lives in the i18n section below
|
|
30
|
+
- Site origin: `site.url` in `nuxt.config` is the single source, read through `useSiteConfig().url` (nuxt-site-config, bundled with `@nuxtjs/seo`) inside the one URL helper the SEO composable and schema use; never a `'https://domain'` literal in a composable, page, component or content record. URL form: `seo.A6`
|
|
30
31
|
- Never call h3's `readBody()` in a DELETE handler. On workerd, reading a body the runtime never actually attached to a DELETE request hangs the promise instead of rejecting it — the platform then kills the request as a bare 500 with no stack trace. This reproduces on real Cloudflare Pages/Workers but NOT under `nuxt dev` (Node), so it survives local testing and only surfaces in production. Pass the id (or any DELETE payload) via query string on both client and server instead — never body.
|
|
31
32
|
- After the response, keep the isolate alive with `event.context.cloudflare.context.waitUntil` (the path live notify modules use on tachnhac / vstshop / tuvi / kinhdich). Nitro 2.13 also wraps the same CF context as `event.waitUntil`. Do not copy Nitro v3 docs onto this stack: v3 puts the platform on `event.req.runtime.cloudflare`, and `event.req.waitUntil` does not exist on 2.13, so the isolate dies. `nuxt dev` cannot prove isolate lifetime. A 200 that returns before the background work is not evidence the work ran.
|
|
32
33
|
|
package/payload/index.md
CHANGED
|
@@ -12,13 +12,13 @@ Provides reusable rules for agent behavior, coding, content, docs, and stack-spe
|
|
|
12
12
|
| `RULE-agent-behavior.md` | `agent` | Core — `@` import in `~/.claude/CLAUDE.md` | public | Core: full text already in context every turn — read it directly, not this summary |
|
|
13
13
|
| `RULE-coding.md` | `coding` | Core — `@` import in `~/.claude/CLAUDE.md` | public | Core: full text already in context every turn — read it directly, not this summary |
|
|
14
14
|
| `RULE-pattern-core.md` | `pattern` | Core — `@` import in `~/.claude/CLAUDE.md` | public | Core: full text already in context every turn — read it directly, not this summary |
|
|
15
|
-
| `RULE-docs.md` | `docs` | Contextual | public | Docs structure (incl. mandatory `docs/biz/` backbone), `updated <date> <version>` anchor stamp on every `arch|biz|feat` doc, the auto-loaded instruction file's five-test admission bar (`A5`: nameable harm, majority-of-requests reach, not derivable, not a restatement, facts not behavior), plan lifecycle, research doc schema (event record: start time/purpose/strategy/checklist/result+verification/decision+cross-refs; frozen body, dated `## Amendments` for errata, successor doc only when the Decision changes), doc-sync behavior, drift audit (when it runs vs the two situations it does not, research+plan doc pair, comparison checklist, wrong/stale/incomplete/cosmetic severity) |
|
|
16
|
-
| `RULE-content-write.md` | `content` | Contextual | public | UI copy, semantic stability, writing style (density enforced by deletion test), i18n, content audit (`content.C2` — canonical-term drift, density deletion test, i18n coverage, severity-classified per `docs.C4`) |
|
|
15
|
+
| `RULE-docs.md` | `docs` | Contextual | public | Docs structure (incl. mandatory `docs/biz/` backbone), `updated <date> <version>` anchor stamp on every `arch|biz|feat` doc and every `ref/fact-*`, fact docs (`A6`: three claim classes — biz decided by the owner, decision reached by research, fact held only by evidence; every fact carries its source with read date or pinned artifact version plus the research section that verified it, changes only through a research event, `content.C2` reads it first), the auto-loaded instruction file's five-test admission bar (`A5`: nameable harm, majority-of-requests reach, not derivable, not a restatement, facts not behavior), plan lifecycle, research doc schema (event record: start time/purpose/strategy/checklist/result+verification/decision+cross-refs; frozen body, dated `## Amendments` for errata, successor doc only when the Decision changes), doc-sync behavior, drift audit (when it runs vs the two situations it does not, research+plan doc pair, comparison checklist, wrong/stale/incomplete/cosmetic severity) |
|
|
16
|
+
| `RULE-content-write.md` | `content` | Contextual | public | UI copy, semantic stability, writing style (density enforced by deletion test), i18n, content audit (`content.C2` — canonical-term drift, density deletion test, i18n coverage, fact-check of product claims against the product's own repo or live page, severity-classified per `docs.C4`) |
|
|
17
17
|
| `RULE-stack-akiNuxtCf.md` | `stack` | Contextual | **mixed** — group C is ⟨Aki⟩ | Nuxt/Vue/Cloudflare Pages/Workers, Tailwind, i18n, canonical component names, state (useState-first), build & TypeScript, admin layout isolation, dev workflow scripts (killport/D1), layout chrome (breadcrumb/scroll-to-top), layout width (single source of truth in the layout, pages/apps never redeclare max-w), deploy verification after push |
|
|
18
18
|
| `RULE-stack-tauri.md` | `tauri` | Contextual | public | Tauri v2 + Rust: absolute never-block-the-UI rule for any command running a subprocess/network call (`spawn_blocking`), titlebar boundary, version SSOT, IPC capability silent-fail, serde default for persisted JSON, cfg(target_os) scoping, subprocess PATH-resolution cold-start race, salient target context (ship platform) surfaced in the project CLAUDE.md, macOS TCC/Gatekeeper boundary for spawned sidecars (responsible-process attribution, FDA vs Files & Folders vs Developer Tools, sticky denials, ad-hoc signing losing grants on every rebuild, and the read-only scope limit of the whole chain) |
|
|
19
19
|
| `RULE-ui-pattern.md` | `ui` | Contextual | public | Frontend enforcement of pattern-core: subtraction pass before any tier (delete/inherit/hoist — the ladder packages repetition, only this removes it), 4-tier class taxonomy with the second copy as the STOP (the ≥3 threshold is repo-wide and unobservable inside one file), inline `style=` as a runtime-only escape hatch, `<style>`-block budget measured in aggregate against the shared layer, design tokens in whichever mechanism the installed framework version uses with one theme source per project, arbitrary-value policy, atomic structure, variant API, two-way lookup-then-record pattern duty, UI audit/refactor playbook led by the inversion check |
|
|
20
|
-
| `RULE-seo.md` | `seo` | Contextual | **mixed** — group C is ⟨Aki⟩ | Meta limits, schema.org matrix, robots, sitemap, OG, AI visibility, entity linking |
|
|
21
|
-
| `RULE-release.md` | `release` | Contextual | **mixed** — group C is ⟨Aki⟩ | CHANGELOG.md mandatory in every project, release notes vs changelog split, GitHub Release compare-link footer, releases.json (web-only), release vs deploy boundary, cold-start version reconstruction, severity-driven bump, version minted only at the release event (`[Unreleased]` buffer, no local drift ahead of production), audit mode, pre-ship gate expanded into the full-release checklist (B7: leftover triage, diff-scoped hygiene, build & test mirroring CI as a mandatory step before verification honesty and the version decision), autonomous-run contract (B8: an explicit release order is the authorization — activation owned by akiship's own gate, this rule is never itself a trigger; asks front-loaded into one batch, three-case escalation floor and completion-intensity definition owned solely by B8, owner-worded criteria decided and reported rather than escalated by default; entry point `/akiship`), registry-published packages (B9: the registry version is the release, publish mechanism derived from existing convention and sibling packages, account/scope/2FA probed, OTP publish as the single hand-off, tarball verified before the irreversible publish), post-push CI watch (B10: a push or tag push is not Done until every triggered workflow is green, red fixed forward with a new commit never a history rewrite), migration doctrine (B5: detect by effect including startup-embedded code, separate artifact, expand → migrate → deploy → contract, rehearse from the PREVIOUS state never from empty, postconditions + named rollback), fail-closed gate contract (B7: a receipt line per step or the step was NOT RUN, self-interrogation reported, forbidden evidence words), post-deploy functional verification (B11: a version string proves code not function, a constant-`ok` health endpoint is a false instrument) |
|
|
20
|
+
| `RULE-seo.md` | `seo` | Contextual | **mixed** — group C is ⟨Aki⟩ | Meta limits, schema.org matrix, robots, sitemap, OG, URL form (relative at rest, absolute only at the emission boundary), AI visibility, entity linking |
|
|
21
|
+
| `RULE-release.md` | `release` | Contextual | **mixed** — group C is ⟨Aki⟩ | CHANGELOG.md mandatory in every project, release notes vs changelog split, one canonical CHANGELOG shape (C1: fixed Keep a Changelog section order and closed vocabulary, no invented headings), `releases.json` highlight tier (C2: the one line a returning user came back for, first in the list, at most two, never a fix), both mechanically checked by `release_lint.py` (C4, wired into the B7 gate), release copy as one text in three lengths with an announce verdict, the last block of every `/akiship` run and the single source for the GitHub Release title/body and the `releases.json` title (B6), GitHub Release compare-link footer, releases.json (web-only), release vs deploy boundary, cold-start version reconstruction, severity-driven bump, version minted only at the release event (`[Unreleased]` buffer, no local drift ahead of production), audit mode, pre-ship gate expanded into the full-release checklist (B7: leftover triage, diff-scoped hygiene, build & test mirroring CI as a mandatory step before verification honesty and the version decision), autonomous-run contract (B8: an explicit release order is the authorization — activation owned by akiship's own gate, this rule is never itself a trigger; asks front-loaded into one batch, three-case escalation floor and completion-intensity definition owned solely by B8, owner-worded criteria decided and reported rather than escalated by default; entry point `/akiship`), registry-published packages (B9: the registry version is the release, publish mechanism derived from existing convention and sibling packages, account/scope/2FA probed, OTP publish as the single hand-off, tarball verified before the irreversible publish), post-push CI watch (B10: a push or tag push is not Done until every triggered workflow is green, red fixed forward with a new commit never a history rewrite), migration doctrine (B5: detect by effect including startup-embedded code, separate artifact, expand → migrate → deploy → contract, rehearse from the PREVIOUS state never from empty, postconditions + named rollback), fail-closed gate contract (B7: a receipt line per step or the step was NOT RUN, self-interrogation reported, forbidden evidence words), post-deploy functional verification (B11: a version string proves code not function, a constant-`ok` health endpoint is a false instrument) |
|
|
22
22
|
| `RULE-db-design.md` | `db` | Contextual | public | Immutability & Event Sourcing, 1NF, Bounded Context (DDD), flat-query discipline — load when designing schema/migration/DB refactor |
|
|
23
23
|
| `RULE-biz.md` | `biz` | Contextual | public | Positioning & audience (one primary audience, falsifiable USP, `docs/biz/` as SSoT, niche-first), offer & pricing (value-based, few tiers, validate before building), messaging & customer psychology (benefit-first, anxiety at decision points, no dark patterns) — load on any market-facing decision |
|
|
24
24
|
| `METHOD-audit-flow.md` | `flow` | Analytical | public | Flow integrity audit method |
|
|
@@ -71,7 +71,7 @@ Some subjects legitimately live in several files: one **root rule** stating the
|
|
|
71
71
|
|---|---|---|
|
|
72
72
|
| **Naming** | `pattern.A7` — name by role, never by concrete value | `agent.C1` file names · `ui.A` design tokens · `stack.C1` ⟨Aki⟩ canonical component names · `release.A3` version/tag format · `content.A3` semantic stability (renaming an existing concept) |
|
|
73
73
|
| **External-action completeness** ("done" needs the outside world to move, not just the file) | `coding.B3` — a change requiring a separate action against an external system isn't done when the file describing it is written | `release.B5` ⟨Aki⟩ CHANGELOG/release entry not truthful until a migration/infra step actually ran · `stack.C8` ⟨Aki⟩ D1 migration must run `--remote` and move to `scripts/done/`, a green build alone proves nothing about the database · `release.B10` a push or tag push is not Done until every triggered CI workflow is confirmed green · `release.B11` a deploy is not Done until a data path the release touched is exercised, not only the version |
|
|
74
|
-
| **Audit reports, never fixes** (and the output depends on whether the baseline is stable) | `agent.B5` — an audit writes only its report; never mutates git state, never auto-classifies ambiguous work | `docs.C` docs-vs-reality, research+plan doc pair on a published baseline · `content.C2` canonical-term drift, density deletion test, i18n coverage sweeps · `release.B7` pre-ship pass/fail gate, no doc · `ui.C` class/token audit playbook · `flow` flow and state drift · `zero-trust` mechanical-first strict sweep, evidence weighted by the mechanism that produced it · `subtract` repo-wide does-this-need-to-exist sweep, terminating on two dry rounds |
|
|
74
|
+
| **Audit reports, never fixes** (and the output depends on whether the baseline is stable) | `agent.B5` — an audit writes only its report; never mutates git state, never auto-classifies ambiguous work | `docs.C` docs-vs-reality, research+plan doc pair on a published baseline · `content.C2` canonical-term drift, density deletion test, i18n coverage, fact-check sweeps · `release.B7` pre-ship pass/fail gate, no doc · `ui.C` class/token audit playbook · `flow` flow and state drift · `zero-trust` mechanical-first strict sweep, evidence weighted by the mechanism that produced it · `subtract` repo-wide does-this-need-to-exist sweep, terminating on two dry rounds |
|
|
75
75
|
| **Sizing a control against its real threat** (severity is impact **and** who can actually reach it) | `proportion.A` — reach, capability, motive, blast radius, each labeled measured or estimated, before any guard is added, kept, or removed | `coding.C1` no defensive guards for impossible internal states · `coding.C4` the security floor this sizing never argues below · `pattern.A2` risk-weighted extraction at the 2nd occurrence for auth/money/permissions · `think.A1` one-way vs two-way door depth · `think.B5` when an edge-case is promoted above the MVP · `ux.C1` findings ranked by severity, never padded flat |
|
|
76
76
|
| **Density — the deletion test** (a line exists only if deleting it loses information the reader needs) | `agent.A4` — report density: conclusion-first, no padding, no trimming of load-bearing detail | `coding.B4` code comments (naming first; comment only what code cannot say) · `docs.B3` doc prose · `docs.A5` the auto-loaded instruction file (deletion test plus a majority-of-requests reach bar, since every line is paid on every request) · `content.B2` product copy · akiflow Step 4 output-hygiene floor (the enforcement tier for subagents, which inherit no router) · mechanical detection: `skills/akiflow/scripts/scythe.py` (`[WRAP]`/`[YAP]` only — `[FLUFF]` stays judgment, `agent` §0) |
|
|
77
77
|
| **Subtraction before abstraction** (packaging repetition is second-best; not needing it is first) | `think.B4` — what can be deleted, skipped, merged, delayed, or made manual | `pattern.B3` first bullet of the critique gate · `ui.A1` delete/inherit/hoist pass ahead of the tier ladder · `subtract` the repo-wide audit form of the same question, read-only and detector-driven · akiflow's `aki-challenger`, which closes every solution-shaped item on "what can be cut?" |
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: aki-article-writer
|
|
3
3
|
description: >-
|
|
4
|
-
Per-project
|
|
5
|
-
JSON-LD
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
Per-project pipeline for publishable site prose: research & fact-verification, SEO
|
|
5
|
+
metadata, JSON-LD, UX-psychology-aware content, a rendered-output pass, and a
|
|
6
|
+
separate Image Scout subagent for images. Activate whenever the task writes,
|
|
7
|
+
rewrites, translates or reviews an article, news or blog post, announcement or
|
|
8
|
+
knowledge entry for any site, in any wording or language, including turning a
|
|
9
|
+
release, changelog or finding into a post.
|
|
10
10
|
---
|
|
11
11
|
|
|
12
12
|
# aki-article-writer
|
|
13
13
|
|
|
14
|
-
Invoke with `/aki-article-writer`, or whenever
|
|
14
|
+
Invoke with `/aki-article-writer`, or whenever a task writes, rewrites, translates or reviews publishable site prose (article, news/blog post, announcement, knowledge entry), in any wording — including a release or finding to be turned into a post.
|
|
15
15
|
|
|
16
16
|
This skill delegates one full article to a dedicated **Article Worker subagent**. The worker spawns a separate **Image Scout subagent** (lightweight model) for all image work, keeping both agents' contexts clean and independent.
|
|
17
17
|
|
|
@@ -92,7 +92,7 @@ Select the correct schema type based on project:
|
|
|
92
92
|
|
|
93
93
|
### 2.5 URL canonical & trailing slash
|
|
94
94
|
|
|
95
|
-
All canonical URLs, sitemap entries, `og:url`, internal links, and JSON-LD `url` fields must end with `/`. Required for Cloudflare Pages compatibility.
|
|
95
|
+
All canonical URLs, sitemap entries, `og:url`, internal links, and JSON-LD `url` fields must end with `/`. Required for Cloudflare Pages compatibility. Those absolute URLs are produced at emission; every URL stored in the record or rendered in the body is site-relative (`seo.A6`).
|
|
96
96
|
|
|
97
97
|
---
|
|
98
98
|
|
|
@@ -130,16 +130,7 @@ Example:
|
|
|
130
130
|
|
|
131
131
|
Use exactly one canonical term for each concept throughout the article. Synonym variation may seem stylistically rich but confuses both readers and AI crawlers. Pick the term, define it once, use it consistently.
|
|
132
132
|
|
|
133
|
-
### 3.4
|
|
134
|
-
|
|
135
|
-
Google treats `vst là gì` and `vst la gi` as different queries. To cover both without degrading readability:
|
|
136
|
-
|
|
137
|
-
- Embed the unaccented form in parentheses at its **first occurrence** in body copy or FAQ: `…VST (vst la gi) là loại phần mềm…`
|
|
138
|
-
- Or place it in `keywords` meta or `alternateName` in schema
|
|
139
|
-
|
|
140
|
-
**Never** place unaccented forms in H1, H2, H3, or FAQ question text — it degrades the visual quality of the interface.
|
|
141
|
-
|
|
142
|
-
### 3.5 Anxiety handling at CTA
|
|
133
|
+
### 3.4 Anxiety handling at CTA
|
|
143
134
|
|
|
144
135
|
At every call-to-action point (sign-up, purchase, download, consult), identify the dominant user anxiety at that moment and answer it right there — not on a distant FAQ page:
|
|
145
136
|
|
|
@@ -150,12 +141,12 @@ At every call-to-action point (sign-up, purchase, download, consult), identify t
|
|
|
150
141
|
| Compatibility | "Hỗ trợ Win/Mac, tương thích mọi DAW phổ biến" |
|
|
151
142
|
| Privacy | "Không lưu dữ liệu cá nhân, xoá tài khoản bất cứ lúc nào" |
|
|
152
143
|
|
|
153
|
-
### 3.
|
|
144
|
+
### 3.5 Internal & external links
|
|
154
145
|
|
|
155
146
|
- **Internal links:** ≥ 2 links to related articles or service pages within the same project
|
|
156
147
|
- **External links:** link to authoritative sources when citing data; add `rel="noopener"`
|
|
157
148
|
|
|
158
|
-
### 3.
|
|
149
|
+
### 3.6 SSR / prerender requirement
|
|
159
150
|
|
|
160
151
|
69% of AI crawlers (ChatGPT, ClaudeBot, PerplexityBot, OAI-SearchBot) do not execute JavaScript. All article content, meta tags, and schema must be present in the server-rendered HTML at crawl time — never client-side only.
|
|
161
152
|
|
|
@@ -331,7 +322,7 @@ Embed with markdown image syntax directly in body content:
|
|
|
331
322
|
### `article_arch: component` (single-image mode)
|
|
332
323
|
|
|
333
324
|
Do **not** write `![]()` anywhere in body/paragraph fields — the renderer does not parse it and will print the literal text. Instead:
|
|
334
|
-
1. Set the content record's existing share-image field (e.g. `ogImage`) to `/images/articles/<slug>.<ext
|
|
325
|
+
1. Set the content record's existing share-image field (e.g. `ogImage`) to `/images/articles/<slug>.<ext>`, site-relative. Confirm the SEO composable and schema make it absolute (`seo.A6`); if either passes it through raw, fix that helper once — never write the site origin into the record, even when neighbouring records do.
|
|
335
326
|
2. Confirm the shared render component (e.g. `ContentArticle.vue`) already displays that field as a hero image above the body. If it does not yet, that is a one-time component change to flag to the user — do not route around it with markdown text in a paragraph.
|
|
336
327
|
3. No separate body images in this mode; one image serves as both hero and OG/share image.
|
|
337
328
|
|
|
@@ -354,7 +345,6 @@ Article Worker runs through the full checklist before reporting completion.
|
|
|
354
345
|
- [ ] No `UNVERIFIED` claim appears in the published text
|
|
355
346
|
- [ ] No banned openers in any paragraph or FAQ answer
|
|
356
347
|
- [ ] No paragraph exceeds 5 lines
|
|
357
|
-
- [ ] Unaccented Vietnamese keyword embedded in parentheses at first occurrence in body / FAQ (vi locale only)
|
|
358
348
|
- [ ] CTA point has an anxiety-answering line
|
|
359
349
|
- [ ] ≥ 1 H2 is a direct question (ends with `?`)
|
|
360
350
|
- [ ] ≥ 2 internal links
|
|
@@ -370,6 +360,12 @@ Article Worker runs through the full checklist before reporting completion.
|
|
|
370
360
|
### 6.4 SSR / prerender
|
|
371
361
|
- [ ] Article content, meta tags, and schema are present in server-rendered HTML, not deferred to client-side JS
|
|
372
362
|
|
|
363
|
+
### 6.5 Rendered-output pass
|
|
364
|
+
Source strings, a green build and a passing SEO validator do not show what the reader sees. After the build, open the article's built HTML (every locale) — the build is self-authorized, no dev server needed (`coding.B3`):
|
|
365
|
+
- [ ] `grep` the page body: no `src`, `srcset` or `<a href>` carries the site's own origin; `og:image` and JSON-LD `image` are absolute (`seo.C3`)
|
|
366
|
+
- [ ] Read the full rendered text top to bottom as the target reader, in each locale, and fix anything that reader would stumble on — before reporting the article done
|
|
367
|
+
- [ ] Any SEO device visible in the text (a parenthetical keyword variant, a repeated keyphrase) is listed as a review line, never shipped silently
|
|
368
|
+
|
|
373
369
|
---
|
|
374
370
|
|
|
375
371
|
## Antigravity / AGY note
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# release_lint.py — mechanical checks for the release record surfaces RULE-release.md owns: CHANGELOG.md shape (C1) and releases.json parity + highlight (C2–C4).
|
|
3
|
+
# Usage: release_lint.py [--latest] [--all] <project-dir|CHANGELOG.md> [...] --latest checks only the newest version block (the B7 gate scope).
|
|
4
|
+
# Output: [TAG] path:line | short label Exit: 0 clean · 1 findings · 2 usage error. Same grammar as scythe.py.
|
|
5
|
+
# Verdict tags: [ORDER] sections out of canonical order · [SECTION] heading outside the closed vocabulary or duplicated · [LEVEL] version/section heading at the wrong level · [PARITY] version present in one surface and absent from the other · [TYPE] releases.json type outside new|improved|fixed|internal.
|
|
6
|
+
# Review tag (never a verdict): [HILITE] a version with a `new` change and no `highlight: true` line, more than two highlighted lines, a highlight not first, or a highlight on a `fixed`/`internal` line — a candidate for C2 judgment.
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
import re
|
|
12
|
+
import sys
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
SECTIONS = ['Added', 'Changed', 'Deprecated', 'Removed', 'Fixed', 'Security']
|
|
16
|
+
TYPES = {'new', 'improved', 'fixed', 'internal'}
|
|
17
|
+
HIGHLIGHT_MAX = 2
|
|
18
|
+
|
|
19
|
+
_VERSION = re.compile(r'^(#+)\s*\[?(v?\d+\.\d+\.\d+[^\]\s]*|Unreleased)\]?', re.I)
|
|
20
|
+
_SECTION = re.compile(r'^(#+)\s*(\S+)')
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _changelog_blocks(lines: list[str]) -> list[dict]:
|
|
24
|
+
"""Split a CHANGELOG into version blocks: {version, line, level, sections:[(name, line, level)]}."""
|
|
25
|
+
blocks: list[dict] = []
|
|
26
|
+
fence = False
|
|
27
|
+
for n, raw in enumerate(lines, 1):
|
|
28
|
+
if raw.lstrip().startswith(('```', '~~~')):
|
|
29
|
+
fence = not fence
|
|
30
|
+
continue
|
|
31
|
+
if fence or not raw.startswith('#'):
|
|
32
|
+
continue
|
|
33
|
+
vm = _VERSION.match(raw)
|
|
34
|
+
if vm and (not blocks or len(vm.group(1)) <= 2 or blocks[-1]['level'] == len(vm.group(1))):
|
|
35
|
+
blocks.append({'version': vm.group(2), 'line': n, 'level': len(vm.group(1)), 'sections': []})
|
|
36
|
+
continue
|
|
37
|
+
sm = _SECTION.match(raw)
|
|
38
|
+
if sm and len(sm.group(1)) == 2 and sm.group(2) != 'Changelog':
|
|
39
|
+
blocks.append({'version': None, 'line': n, 'level': 2, 'sections': [], 'heading': raw.lstrip('# ').strip()})
|
|
40
|
+
continue
|
|
41
|
+
if sm and blocks and blocks[-1]['version'] and len(sm.group(1)) > blocks[-1]['level']:
|
|
42
|
+
blocks[-1]['sections'].append((sm.group(2).strip('*').rstrip(':'), n, len(sm.group(1))))
|
|
43
|
+
return blocks
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def lint_changelog(path: Path, latest: bool) -> tuple[list[str], list[str]]:
|
|
47
|
+
lines = path.read_text(encoding='utf-8', errors='replace').splitlines()
|
|
48
|
+
blocks = _changelog_blocks(lines)
|
|
49
|
+
if latest:
|
|
50
|
+
blocks = blocks[:1]
|
|
51
|
+
findings: list[str] = []
|
|
52
|
+
for b in blocks:
|
|
53
|
+
if b['version'] is None:
|
|
54
|
+
findings.append(f"[SECTION] {path}:{b['line']} | H2 `{b['heading']}` is not a version heading")
|
|
55
|
+
continue
|
|
56
|
+
if b['level'] != 2:
|
|
57
|
+
findings.append(f"[LEVEL] {path}:{b['line']} | version heading at H{b['level']}, expected `## [{b['version']}] - <date>`")
|
|
58
|
+
names = [s[0] for s in b['sections']]
|
|
59
|
+
seen: set[str] = set()
|
|
60
|
+
for name, n, lvl in b['sections']:
|
|
61
|
+
if name not in SECTIONS:
|
|
62
|
+
findings.append(f"[SECTION] {path}:{n} | `{name}` is not one of {', '.join(SECTIONS)}")
|
|
63
|
+
elif name in seen:
|
|
64
|
+
findings.append(f"[SECTION] {path}:{n} | `{name}` repeated inside {b['version']}")
|
|
65
|
+
elif lvl != b['level'] + 1:
|
|
66
|
+
findings.append(f"[LEVEL] {path}:{n} | section at H{lvl}, expected H{b['level'] + 1}")
|
|
67
|
+
seen.add(name)
|
|
68
|
+
std = [x for x in names if x in SECTIONS]
|
|
69
|
+
if std != sorted(dict.fromkeys(std), key=SECTIONS.index) and len(set(std)) == len(std):
|
|
70
|
+
expected = ', '.join(sorted(std, key=SECTIONS.index))
|
|
71
|
+
findings.append(f"[ORDER] {path}:{b['line']} | {b['version']}: {', '.join(std)} — expected {expected}")
|
|
72
|
+
return findings, [b['version'].lstrip('v') for b in blocks if b['version']]
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def lint_releases(path: Path, changelog_versions: list[str], latest: bool) -> list[str]:
|
|
76
|
+
findings: list[str] = []
|
|
77
|
+
try:
|
|
78
|
+
data = json.loads(path.read_text(encoding='utf-8'))
|
|
79
|
+
except (OSError, json.JSONDecodeError) as e:
|
|
80
|
+
return [f"[PARITY] {path}:1 | unreadable releases.json ({e})"]
|
|
81
|
+
items = data if isinstance(data, list) else data.get('releases', [])
|
|
82
|
+
if latest:
|
|
83
|
+
items = items[:1]
|
|
84
|
+
text = path.read_text(encoding='utf-8').splitlines()
|
|
85
|
+
|
|
86
|
+
def line_of(version: str) -> int:
|
|
87
|
+
for n, raw in enumerate(text, 1):
|
|
88
|
+
if f'"{version}"' in raw:
|
|
89
|
+
return n
|
|
90
|
+
return 1
|
|
91
|
+
|
|
92
|
+
json_versions = [str(r.get('version', '')).lstrip('v') for r in items]
|
|
93
|
+
for v in [x for x in changelog_versions if x.lower() != 'unreleased']:
|
|
94
|
+
if v not in json_versions:
|
|
95
|
+
findings.append(f"[PARITY] {path}:1 | CHANGELOG version {v} has no releases.json entry")
|
|
96
|
+
for r, v in zip(items, json_versions):
|
|
97
|
+
n = line_of(v)
|
|
98
|
+
if v not in changelog_versions:
|
|
99
|
+
findings.append(f"[PARITY] {path}:{n} | releases.json version {v} has no CHANGELOG entry")
|
|
100
|
+
changes = r.get('changes', [])
|
|
101
|
+
for c in changes:
|
|
102
|
+
if c.get('type') not in TYPES:
|
|
103
|
+
findings.append(f"[TYPE] {path}:{n} | {v}: type `{c.get('type')}` not in {', '.join(sorted(TYPES))}")
|
|
104
|
+
hl = [c for c in changes if c.get('highlight')]
|
|
105
|
+
if not hl and any(c.get('type') == 'new' for c in changes):
|
|
106
|
+
findings.append(f"[HILITE] {path}:{n} | {v}: has a `new` change but no `highlight: true` (review)")
|
|
107
|
+
if len(hl) > HIGHLIGHT_MAX:
|
|
108
|
+
findings.append(f"[HILITE] {path}:{n} | {v}: {len(hl)} highlighted lines, more than {HIGHLIGHT_MAX} (review)")
|
|
109
|
+
if hl and changes and not changes[0].get('highlight'):
|
|
110
|
+
findings.append(f"[HILITE] {path}:{n} | {v}: highlighted line is not the first change (review)")
|
|
111
|
+
for c in hl:
|
|
112
|
+
if c.get('type') in ('fixed', 'internal'):
|
|
113
|
+
findings.append(f"[HILITE] {path}:{n} | {v}: highlight on a `{c.get('type')}` change, C2 allows only new/improved (review)")
|
|
114
|
+
return findings
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def lint_target(target: str, latest: bool) -> list[str]:
|
|
118
|
+
p = Path(target)
|
|
119
|
+
changelog = p if p.is_file() else p / 'CHANGELOG.md'
|
|
120
|
+
if not changelog.is_file():
|
|
121
|
+
print(f"release_lint: no CHANGELOG.md at {target}", file=sys.stderr)
|
|
122
|
+
sys.exit(2)
|
|
123
|
+
findings, versions = lint_changelog(changelog, latest)
|
|
124
|
+
releases = changelog.parent / 'app' / 'data' / 'releases.json'
|
|
125
|
+
if releases.is_file():
|
|
126
|
+
findings.extend(lint_releases(releases, versions, latest))
|
|
127
|
+
return findings
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def main() -> None:
|
|
131
|
+
latest = '--latest' in sys.argv
|
|
132
|
+
show_all = '--all' in sys.argv
|
|
133
|
+
targets = [a for a in sys.argv[1:] if not a.startswith('--')]
|
|
134
|
+
if not targets:
|
|
135
|
+
print("usage: release_lint.py [--latest] [--all] <project-dir|CHANGELOG.md> [...]", file=sys.stderr)
|
|
136
|
+
sys.exit(2)
|
|
137
|
+
findings: list[str] = []
|
|
138
|
+
for t in targets:
|
|
139
|
+
findings.extend(lint_target(t, latest))
|
|
140
|
+
if not findings:
|
|
141
|
+
sys.exit(0)
|
|
142
|
+
cap = 40
|
|
143
|
+
shown = findings if show_all or len(findings) <= cap else findings[:cap]
|
|
144
|
+
for line in shown:
|
|
145
|
+
print(line)
|
|
146
|
+
if len(shown) < len(findings):
|
|
147
|
+
counts: dict[str, int] = {}
|
|
148
|
+
for line in findings:
|
|
149
|
+
tag = line.split()[0]
|
|
150
|
+
counts[tag] = counts.get(tag, 0) + 1
|
|
151
|
+
print(f"--- {len(findings) - cap} more findings suppressed ---")
|
|
152
|
+
print(' '.join(f"{t} {c}" for t, c in counts.items()) + f" (total {len(findings)})")
|
|
153
|
+
sys.exit(1)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
if __name__ == '__main__':
|
|
157
|
+
main()
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: akiopen
|
|
3
|
+
description: Session-opening brief — what is still pending in this project, and nothing else. Use whenever a session opens on a project or the owner asks, in any wording, what is unfinished, what the active plans or notes say, what an inbox file still lists, or what to pick up next. Read-only; reports only what needs doing, in a fixed four-field shape, ranked by severity.
|
|
4
|
+
user-invocable: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# akiopen — what is pending here
|
|
8
|
+
|
|
9
|
+
Opening a project means rebuilding the picture of unfinished work from five places nobody should have to remember (`ux.A2`). This skill reads all five in one pass and reports only what still needs doing. It is an audit (`agent.B5`): no file edits, no git mutation, no note or plan marked done.
|
|
10
|
+
|
|
11
|
+
## Scan — one batched pass, only surfaces that exist
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
git status --short && git diff --stat | tail -1
|
|
15
|
+
python3 ~/.claude/skills/akidevsync-notes/scripts/notes_cli.py .akidevsync/notes.json list --pending --detail
|
|
16
|
+
grep -c '^\s*- \[ \]' docs/plan/*.md docs/*.md /dev/null 2>/dev/null | grep -v ':0$'
|
|
17
|
+
awk '/^## \[Unreleased\]/{f=1;next} /^## \[/{f=0} f' CHANGELOG.md | grep -c '^- '
|
|
18
|
+
grep -rn -i 'needs owner\|needs mac\|manual test\|unverified' docs/plan/*.md 2>/dev/null
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
| Surface | Pending means |
|
|
22
|
+
|---|---|
|
|
23
|
+
| Working tree | modified or untracked files; group them by directory, not one line per file |
|
|
24
|
+
| Notes | `.akidevsync/notes.json` tasks with `done: false`; pinned first |
|
|
25
|
+
| Active plans | `docs/plan/*.md` outside `done/` with open `- [ ]` items, or a plan whose text says done but still sits outside `done/` (`docs.B1`) |
|
|
26
|
+
| Inbox files | any top-level `docs/*.md` with open `- [ ]` items — tasks pushed in from an upstream standard or another repo |
|
|
27
|
+
| Release | `[Unreleased]` has entries, or the tree changed with no entry yet (`release.A`) |
|
|
28
|
+
| Hand-offs | a line in an active plan waiting on the owner or another machine (`coding.B5`) |
|
|
29
|
+
|
|
30
|
+
A surface the project does not have is skipped silently. A project `CLAUDE.md` may name additional inbox or plan paths; read it before scanning.
|
|
31
|
+
|
|
32
|
+
## Report — pending only, never an inventory
|
|
33
|
+
|
|
34
|
+
- One item per pending thing, at most seven, ranked by severity (`ux.C1`); the rest collapse into one count line.
|
|
35
|
+
- Each item carries four fields: **problem** (what is pending, `path:line`) · **why it is still open** (from the plan text, note age, git dates — measured, never guessed; write *unclear* when the sources do not say) · **proposal** (one sentence) · **goal** (what closes it).
|
|
36
|
+
- Nothing pending: one line, nothing else.
|
|
37
|
+
- Half-finished work that cannot be told from an abandoned experiment is reported as unclassified and asked about, never sorted by guess (`agent.B5`).
|
|
38
|
+
- The report is the deliverable. Fixing anything, including moving a finished plan to `done/`, is a separate request.
|
package/skills/akirule/SKILL.md
CHANGED
|
@@ -29,11 +29,11 @@ All files live in `~/.aki/akidevrule/`.
|
|
|
29
29
|
| File | Load when the task … | Signals — each stands for a concept; any synonym, in any language, counts the same (EN · VI) |
|
|
30
30
|
|---|---|---|
|
|
31
31
|
| `RULE-docs.md` | creates, edits, moves or completes any Markdown, doc, plan, instruction file (`CLAUDE.md`, `SKILL.md`, `README`, `CHANGELOG`), or checks docs against the code | any `.md`, `docs/**`, `SKILL.md`; docs, plan, README, diagram, mermaid, architecture, drift, stale/outdated docs · tài liệu, sơ đồ, kiến trúc, lệch, lỗi thời, rà soát tài liệu |
|
|
32
|
-
| `RULE-content-write.md` | writes, renames or audits text an end user reads — UI copy, messages, labels, i18n strings, page metadata copy | button/label/heading, error message, tooltip, empty state, tone, i18n, locale, translation, `locales/**`, renaming a user-facing term · nội dung giao diện, nhãn, thông báo lỗi, bản dịch |
|
|
32
|
+
| `RULE-content-write.md` | writes, renames, translates or audits text an end user reads — UI copy, messages, labels, i18n strings, page metadata copy, articles and posts | button/label/heading, error message, tooltip, empty state, tone, i18n, locale, translation, `locales/**`, renaming a user-facing term, article, blog/news post, announcement, content data file (`posts.ts`, `content/**`) · nội dung giao diện, nhãn, thông báo lỗi, bản dịch, bài viết, tin tức, bài đăng |
|
|
33
33
|
| `RULE-stack-akiNuxtCf.md` | works in a Nuxt / Vue / Cloudflare Pages-Workers project — ON for the whole project when its binding names that stack | `.vue`, `nuxt.config`, `wrangler.toml`, Nuxt, Vue, Cloudflare Workers/Pages, D1, KV, Nitro, composable, middleware, `useFetch`, breadcrumb, layout width |
|
|
34
34
|
| `RULE-stack-tauri.md` | works in a Tauri / Rust desktop project — ON for the whole project | `.rs`, `src-tauri/`, `tauri.conf.json`, `Cargo.toml`, `#[tauri::command]`, IPC, `spawn_blocking`, freeze, hang, blocking UI, settings breaking after an upgrade · treo app, đứng app, đơ, khựng |
|
|
35
35
|
| `RULE-ui-pattern.md` | builds, styles, minimizes or audits frontend components, classes, tokens or style blocks | `.vue`/`.css`/`.scss`/`.tsx`, Tailwind, class, style block, inline style, design token, variant, `@apply`, `@theme`, arbitrary value, duplicate/bloated CSS, looks inconsistent · dọn CSS, class trùng, tối giản CSS, nhiều CSS quá |
|
|
36
|
-
| `RULE-seo.md` | shapes how pages are found or represented — metadata, structured data, sitemap/robots, canonical/hreflang, search or AI visibility, entity identity | SEO, meta title/description, OG image, JSON-LD, schema.org, sitemap, robots, canonical, hreflang, trailing slash, AI visibility, not indexed · không lên Google |
|
|
36
|
+
| `RULE-seo.md` | shapes how pages are found or represented — metadata, structured data, sitemap/robots, canonical/hreflang, search or AI visibility, entity identity | SEO, meta title/description, OG image, JSON-LD, schema.org, sitemap, robots, canonical, hreflang, trailing slash, absolute vs relative URL, image path, AI visibility, not indexed · không lên Google, link ảnh |
|
|
37
37
|
| `RULE-release.md` | records, versions, commits, pushes, tags, publishes, deploys or migrates; watches CI or verifies a deploy; or asks whether finished work is shippable | commit, push, deploy, tag, release, release notes, `CHANGELOG`, version, semver, bump, publish, npm/registry, 2FA/OTP, CI, GitHub Actions, migration, post-deploy, health check, "is it done / ready to ship?" · phát hành, phiên bản, nâng version, đẩy lên, triển khai, xong chưa, CI đỏ |
|
|
38
38
|
| `RULE-db-design.md` | designs or changes the shape of stored data — schema, migration, query structure, data refactor | `.sql`, `migrations/`, schema, table, column, index, D1, SQL, ERD, event sourcing, normalization, keeping history of a value, choosing a database · thiết kế DB, đổi schema, thêm cột |
|
|
39
39
|
| `RULE-biz.md` | makes a market-facing decision — audience, positioning, pricing, offer, sales/landing copy, `docs/biz/` | pricing, plan/tier, subscription, monetization, revenue, positioning, USP, target audience, customer, market, conversion, landing page, `docs/biz/` · định giá, gói, khách hàng, thị trường, định vị, doanh thu |
|
|
@@ -45,6 +45,8 @@ All files live in `~/.aki/akidevrule/`.
|
|
|
45
45
|
| `METHOD-audit-subtraction.md` | asks to minimize, strip or clean out what no longer needs to exist | dead code, unused, unreferenced, bloat, redundant guard or fallback, comment restating a known fact, strip down, lean as possible, heavy cleanup · code chết, code thừa, hiển nhiên, tối giản tối đa, dọn sạch repo, tinh gọn |
|
|
46
46
|
| `METHOD-audit-frozen-reference.md` | judges conformance to a concrete reference implementation (another repo, a pinned version, a specific file) at any strictness | frozen/pinned reference, canonical implementation, reference project, template repo, byte-identical, structurally identical, drifted from the original · đối chiếu, giống hệt, y hệt, lệch chuẩn, khớp chuẩn, so với dự án gốc |
|
|
47
47
|
|
|
48
|
+
**Publishable writing** — a task that writes, rewrites or translates an article, news or blog post, announcement or knowledge entry, in any wording, including "turn this release/finding into a post": invoke the `aki-article-writer` skill (the procedure) and load `content` and `seo` (the rules).
|
|
49
|
+
|
|
48
50
|
**Sequential full audit** — the task asks to check a codebase thoroughly across every standard, one after another: load `zero-trust`, `flow`, `subtract`, `docs`, `content`, plus `ui` for a frontend, and run the passes in this order, each read-only: detectors (`zero-trust.B`) → structure (`pattern` laws, `flow`) → subtraction (`subtract`) → docs drift both directions (`docs.C`) → content (`content.C2`). One report, severity-ranked; fixes are a separate run (`agent.B5`).
|
|
49
51
|
|
|
50
52
|
**Deep-think depth** — when the `deep-think` route fires and the decision is a one-way door, the goal is unclear, it changes documented design or shared rules, or an `agent.A3` trigger holds: run `/akithink` in self-run mode without asking. The interactive session runs only when the owner asks for one.
|
package/skills/akiship/SKILL.md
CHANGED
|
@@ -45,6 +45,7 @@ Consult is the default whenever both readings are available. A withheld executio
|
|
|
45
45
|
Run B7 steps 2–7 in order, fixing findings as they surface (this is a gate, not an audit — no findings doc):
|
|
46
46
|
|
|
47
47
|
- **Hygiene, diff scope only**: `python3 ~/.claude/skills/akiflow/scripts/scythe.py <files changed since boundary>` for `[WRAP]`/`[YAP]`; dead code / redundant guards / duplication the accumulation introduced (`pattern.A8`); doc refs in touched comments still resolve (`docs.B3`). Never widen to the whole repo.
|
|
48
|
+
- **Record shape (B7 step 4)**: `python3 ~/.claude/skills/akiflow/scripts/release_lint.py --latest .` — verdict tags fixed in place; each `[HILITE]` line gets a written answer in the receipt (`release.C2`).
|
|
48
49
|
- **Migration & external-action completeness — FIRST gate step, every release.** Run the `release.B5` detector over the accumulation diff and paste its output. A hit (startup-embedded migration code included) obliges written answers to B5 points 2–5, including a rehearsal from the PREVIOUS state; a pending migration qualifying under `stack.C8`'s execution-ownership clause is run here, not deferred. Then record truthfulness (CHANGELOG + `releases.json` parity where it exists) and doc sync over every record surface B7 step 5 enumerates (plans → `done/`, `arch`/`feat` stamps per `docs.A4`, `README.md`, the task-note file via `akidevsync-notes`, any standards doc the project `CLAUDE.md` binds).
|
|
49
50
|
- **Build & test — mirror CI (B7 step 6)**: derive commands from `.github/workflows/*` first, else the manifest's own scripts; run them all locally; a failure blocks and is fixed in place, same as the hygiene step above; a CI-only leg (other-OS matrix, secrets) is named and left to `release.B10`.
|
|
50
51
|
- Verification honesty — anything else runtime-only, or a migration that does not qualify above, is carried to the final report as **unverified**, never silently assumed (`coding.B3`).
|
|
@@ -62,6 +63,8 @@ Run B7 steps 2–7 in order, fixing findings as they surface (this is a gate, no
|
|
|
62
63
|
|
|
63
64
|
Then one dense summary (`agent.A4`): state derived → findings fixed (counts per gate step) → commits made → version minted or deferred with the reason → artifacts created → CI results (`release.B10`) → any owner-worded criteria self-decided this run, as an `agent.A3` decision block (`Decided: X · because Y · rejected Z (why) · reopen if W`) → anything left **unverified**, each with the exact command that would settle it.
|
|
64
65
|
|
|
66
|
+
**The LAST block is the release copy, every run, in `release.B6`'s shape** — Headline, Short, Full, and the announce verdict — quoting the `releases.json` entry and GitHub Release body the run already wrote rather than composing a third text; a deferred version prints `deferred — no copy`. It is the owner's paste-ready text for whatever channel they announce on (a post, a notification, a store listing); the block never names a channel the project's own records do not.
|
|
67
|
+
|
|
65
68
|
## Boundaries
|
|
66
69
|
|
|
67
70
|
- Never write `PASS` on a gate step without quoted evidence (`release.B7` fail-closed contract). "Should", "presumably", "looks fine" score `unverified`.
|