@ersbeth/codelife 0.0.0-bootstrap.0 → 0.11.1
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/.claude-plugin/plugin.json +19 -0
- package/LICENSE +21 -0
- package/README.md +66 -0
- package/agents/evaluate.md +97 -0
- package/agents/ground.md +49 -0
- package/agents/implementer.md +49 -0
- package/agents/judge.md +128 -0
- package/agents/planner.md +45 -0
- package/agents/prerequisites.md +62 -0
- package/agents/reader.md +40 -0
- package/agents/reproduce.md +65 -0
- package/bin/codelife +4 -0
- package/package.json +40 -1
- package/presets/tsconfig.json +14 -0
- package/presets/vite.config.d.mts +3 -0
- package/presets/vite.config.mjs +49 -0
- package/scripts/audit/cadence.mjs +24 -0
- package/scripts/audit/close.mjs +57 -0
- package/scripts/audit/file.mjs +70 -0
- package/scripts/audit/fold.mjs +48 -0
- package/scripts/audit/lib.mjs +93 -0
- package/scripts/audit/prepare.mjs +180 -0
- package/scripts/audit/table.mjs +65 -0
- package/scripts/brainstorm/decided.mjs +6 -0
- package/scripts/brainstorm/lib.mjs +33 -0
- package/scripts/brainstorm/named.mjs +9 -0
- package/scripts/changelog.mjs +100 -0
- package/scripts/check-tag.mjs +19 -0
- package/scripts/checks.mjs +53 -0
- package/scripts/codelife.mjs +42 -0
- package/scripts/commit-msg.mjs +43 -0
- package/scripts/contract.mjs +179 -0
- package/scripts/former-form.mjs +170 -0
- package/scripts/git/branches.mjs +6 -0
- package/scripts/git/commit.mjs +40 -0
- package/scripts/git/diff.mjs +17 -0
- package/scripts/git/diff_lines.mjs +5 -0
- package/scripts/git/held.mjs +53 -0
- package/scripts/git/is_ancestor.mjs +28 -0
- package/scripts/git/lib.mjs +20 -0
- package/scripts/git/log_since.mjs +27 -0
- package/scripts/git/parent.mjs +51 -0
- package/scripts/git/show.mjs +4 -0
- package/scripts/git/status.mjs +25 -0
- package/scripts/git/submodules.mjs +5 -0
- package/scripts/init.mjs +21 -0
- package/scripts/install.mjs +32 -0
- package/scripts/lenses/A11Y.md +91 -0
- package/scripts/lenses/ARCH.md +90 -0
- package/scripts/lenses/BUG.md +52 -0
- package/scripts/lenses/BUILD.md +40 -0
- package/scripts/lenses/COMPAT.md +67 -0
- package/scripts/lenses/CONF.md +49 -0
- package/scripts/lenses/DEPS.md +43 -0
- package/scripts/lenses/DOC.md +73 -0
- package/scripts/lenses/FAIL.md +57 -0
- package/scripts/lenses/GOV.md +56 -0
- package/scripts/lenses/GUARD.md +47 -0
- package/scripts/lenses/INTENT.md +52 -0
- package/scripts/lenses/LEAN.md +49 -0
- package/scripts/lenses/LINES.md +48 -0
- package/scripts/lenses/PERF.md +61 -0
- package/scripts/lenses/PKG.md +58 -0
- package/scripts/lenses/SEC.md +49 -0
- package/scripts/lenses/STANDARDS.md +62 -0
- package/scripts/lenses/TYPE.md +56 -0
- package/scripts/lenses/VERIF.md +81 -0
- package/scripts/link.mjs +40 -0
- package/scripts/methods/cause.md +21 -0
- package/scripts/methods/comments.md +157 -0
- package/scripts/methods/description.md +67 -0
- package/scripts/methods/message.md +24 -0
- package/scripts/methods/red-green.md +28 -0
- package/scripts/methods/refusal.md +22 -0
- package/scripts/mr/open.mjs +219 -0
- package/scripts/node-floor.mjs +19 -0
- package/scripts/onboard/prep.mjs +93 -0
- package/scripts/overview/codelife-line.mjs +33 -0
- package/scripts/overview/next.mjs +76 -0
- package/scripts/overview/overview.mjs +308 -0
- package/scripts/package-manager.mjs +20 -0
- package/scripts/port/item.mjs +94 -0
- package/scripts/port/lib.mjs +194 -0
- package/scripts/port/prep.mjs +33 -0
- package/scripts/port/version.mjs +50 -0
- package/scripts/pre-commit.mjs +43 -0
- package/scripts/pre-push.mjs +53 -0
- package/scripts/prepare-lib.mjs +81 -0
- package/scripts/read-roadmap/lib.mjs +116 -0
- package/scripts/read-roadmap/moved.mjs +70 -0
- package/scripts/read-roadmap/range.mjs +15 -0
- package/scripts/release/cut.mjs +120 -0
- package/scripts/release/entry.mjs +52 -0
- package/scripts/release/lib.mjs +136 -0
- package/scripts/release/prep.mjs +58 -0
- package/scripts/review-mr/prepare.mjs +163 -0
- package/scripts/roadmap/assemble.mjs +269 -0
- package/scripts/roadmap/cut.mjs +88 -0
- package/scripts/roadmap/prepare.mjs +249 -0
- package/scripts/roadmap/roadmap.md +30 -0
- package/scripts/roadmap/write.mjs +53 -0
- package/scripts/search.mjs +29 -0
- package/scripts/setup/contract.mjs +25 -0
- package/scripts/setup/item.mjs +166 -0
- package/scripts/setup/lib.mjs +205 -0
- package/scripts/setup/prep.mjs +106 -0
- package/scripts/spec-form.mjs +444 -0
- package/scripts/spec.mjs +56 -0
- package/scripts/tags.mjs +27 -0
- package/scripts/target/target-app.md +134 -0
- package/scripts/target/target-base.md +127 -0
- package/scripts/target/target-lib.md +143 -0
- package/scripts/target/templates/audit/README.md +21 -0
- package/scripts/target/templates/characterisation/bundle.test.ts +43 -0
- package/scripts/target/templates/characterisation/declarations.test.ts +44 -0
- package/scripts/target/templates/characterisation/entries.ts +50 -0
- package/scripts/target/templates/characterisation/examples.test.ts +18 -0
- package/scripts/target/templates/characterisation/import.test.ts +22 -0
- package/scripts/target/templates/characterisation/surface.test.ts +68 -0
- package/scripts/target/templates/cliff.toml +61 -0
- package/scripts/target/templates/githooks/commit-msg +9 -0
- package/scripts/target/templates/githooks/pre-commit +9 -0
- package/scripts/target/templates/githooks/pre-push +9 -0
- package/scripts/target/templates/playground/index.html +11 -0
- package/scripts/target/templates/playground/main.ts +9 -0
- package/scripts/target/templates/spec/README.md +35 -0
- package/scripts/target/templates/spec/capability.md +36 -0
- package/scripts/target/templates/spec/glossary.md +5 -0
- package/scripts/target/templates/spec/journey.md +29 -0
- package/scripts/target/templates/spec/principles.md +15 -0
- package/scripts/target/templates/spec/users.md +15 -0
- package/scripts/target/templates/tsconfig.json +9 -0
- package/scripts/target/templates/vite.config.ts +4 -0
- package/scripts/text.mjs +6 -0
- package/scripts/tracker/account.mjs +5 -0
- package/scripts/tracker/auth.mjs +9 -0
- package/scripts/tracker/cli.version.mjs +5 -0
- package/scripts/tracker/close.mjs +20 -0
- package/scripts/tracker/create.mjs +47 -0
- package/scripts/tracker/issue.section.mjs +25 -0
- package/scripts/tracker/issue.update.mjs +13 -0
- package/scripts/tracker/issue.view.mjs +16 -0
- package/scripts/tracker/lib.mjs +130 -0
- package/scripts/tracker/list.mjs +14 -0
- package/scripts/tracker/list.whole.mjs +20 -0
- package/scripts/tracker/lot.row.mjs +42 -0
- package/scripts/tracker/mr.checkout_command.mjs +5 -0
- package/scripts/tracker/mr.comments.mjs +50 -0
- package/scripts/tracker/mr.create.mjs +12 -0
- package/scripts/tracker/mr.description.mjs +8 -0
- package/scripts/tracker/mr.fetch_head.mjs +5 -0
- package/scripts/tracker/mr.find_for_branch.mjs +9 -0
- package/scripts/tracker/mr.post.mjs +40 -0
- package/scripts/tracker/mr.update_description.mjs +8 -0
- package/scripts/tracker/mr.verdict.mjs +34 -0
- package/scripts/tracker/mr.view.mjs +20 -0
- package/scripts/tracker/none.mjs +79 -0
- package/scripts/tracker/note.mjs +12 -0
- package/scripts/tracker/reopen.mjs +8 -0
- package/scripts/tracker/search.mjs +8 -0
- package/scripts/tracker/search_id.mjs +18 -0
- package/scripts/tracker/templates/bug.md +35 -0
- package/scripts/tracker/templates/feature.md +40 -0
- package/scripts/tracker/templates/lot.md +46 -0
- package/scripts/tracker/title.join.mjs +6 -0
- package/scripts/tracker/title.leave.mjs +7 -0
- package/scripts/tracker/title.lot_of.mjs +8 -0
- package/scripts/tracker/title.search_id_filter.mjs +17 -0
- package/scripts/tracker/title.sentence_of.mjs +5 -0
- package/scripts/tracker/view.mjs +25 -0
- package/scripts/verbs.mjs +579 -0
- package/scripts/verify/BUILD.mjs +33 -0
- package/scripts/verify/DEPS.mjs +15 -0
- package/scripts/verify/GOV.mjs +34 -0
- package/scripts/verify/GUARD.mjs +34 -0
- package/scripts/verify/LINES.mjs +22 -0
- package/scripts/verify/PKG.mjs +56 -0
- package/scripts/verify/TYPE.mjs +34 -0
- package/scripts/verify/cut.mjs +73 -0
- package/scripts/verify/history.mjs +32 -0
- package/scripts/verify/lib.mjs +129 -0
- package/scripts/verify/report.mjs +104 -0
- package/scripts/verify/timing.mjs +19 -0
- package/scripts/verify.mjs +29 -0
- package/scripts/work/branch.mjs +81 -0
- package/scripts/work/close.mjs +74 -0
- package/scripts/work/member.mjs +73 -0
- package/scripts/work/prepare.mjs +116 -0
- package/scripts/work/reopen.mjs +40 -0
- package/scripts/work-feedback/prepare.mjs +96 -0
- package/skills/audit/SKILL.md +110 -0
- package/skills/audit/templates/index.md +36 -0
- package/skills/audit/templates/lens.md +44 -0
- package/skills/brainstorm/SKILL.md +144 -0
- package/skills/brainstorm/references/exit.md +57 -0
- package/skills/brainstorm/templates/note.md +79 -0
- package/skills/commit/SKILL.md +68 -0
- package/skills/mr/SKILL.md +82 -0
- package/skills/new-issue/SKILL.md +87 -0
- package/skills/onboard/SKILL.md +103 -0
- package/skills/onboard/templates/CLAUDE.md +31 -0
- package/skills/onboard/templates/CONTRIBUTING.md +34 -0
- package/skills/onboard/templates/README.md +30 -0
- package/skills/onboard/templates/SKILL.md +24 -0
- package/skills/onboard/templates/package-CLAUDE.md +15 -0
- package/skills/overview/SKILL.md +59 -0
- package/skills/port-toolchain/SKILL.md +127 -0
- package/skills/release/SKILL.md +78 -0
- package/skills/review-mr/SKILL.md +106 -0
- package/skills/review-mr/templates/pass.md +29 -0
- package/skills/roadmap/SKILL.md +137 -0
- package/skills/setup/SKILL.md +112 -0
- package/skills/work/SKILL.md +139 -0
- package/skills/work-feedback/SKILL.md +113 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// git.status(): the branch, its upstream, ahead and behind, and the three sets, as JSON — read from
|
|
2
|
+
// `git status --porcelain=v2 --branch -uall -z`; a name holding a newline answers `unreadable`.
|
|
3
|
+
import { git, records } from './lib.mjs';
|
|
4
|
+
|
|
5
|
+
const s = { oid: '', branch: '', upstream: '', ahead: 0, behind: 0, staged: [], modified: [], untracked: [] };
|
|
6
|
+
const r = records(git('status', '--porcelain=v2', '--branch', '-uall', '-z'));
|
|
7
|
+
for (let i = 0; i < r.length; i++) {
|
|
8
|
+
const e = r[i], m = e.match(/^([12u]) (..) /);
|
|
9
|
+
if (e.startsWith('# branch.oid ')) s.oid = e.slice(13);
|
|
10
|
+
else if (e.startsWith('# branch.head ')) s.branch = e.slice(14);
|
|
11
|
+
else if (e.startsWith('# branch.upstream ')) s.upstream = e.slice(18);
|
|
12
|
+
else if (e.startsWith('# branch.ab ')) [s.ahead, s.behind] = e.slice(12).split(' ').map((x) => Math.abs(+x));
|
|
13
|
+
else if (e.startsWith('? ')) s.untracked.push(e.slice(2));
|
|
14
|
+
else if (m) {
|
|
15
|
+
const path = e.split(' ').slice({ 1: 8, 2: 9, u: 10 }[m[1]]).join(' ');
|
|
16
|
+
if (m[1] === '2') i++;
|
|
17
|
+
if (m[2][0] !== '.') s.staged.push(path);
|
|
18
|
+
if (m[2][1] !== '.') s.modified.push(path);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
if ([...s.staged, ...s.modified, ...s.untracked].some((p) => p.includes('\n'))) {
|
|
22
|
+
console.log('unreadable');
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
console.log(JSON.stringify(s));
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// git.submodules(commit): the paths of the submodules a commit's tree holds — its gitlinks — as JSON.
|
|
2
|
+
import { arg, git, records } from './lib.mjs';
|
|
3
|
+
|
|
4
|
+
const tree = records(git('ls-tree', '-r', '-z', arg(0, '<commit>')));
|
|
5
|
+
console.log(JSON.stringify(tree.filter((e) => e.startsWith('160000 ')).map((e) => e.slice(e.indexOf('\t') + 1))));
|
package/scripts/init.mjs
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// The first installation, `pnpm dlx @ersbeth/codelife init` (R-272): adds the package as a
|
|
3
|
+
// dependency of the repository it runs in and links it, and nothing else — no contract, no hooks,
|
|
4
|
+
// no `prepare`; `/setup` does the rest. An optional argument is the package spec to add, the
|
|
5
|
+
// published version this file belongs to by default.
|
|
6
|
+
import './node-floor.mjs'
|
|
7
|
+
import { readFileSync } from 'node:fs'
|
|
8
|
+
import { spawnSync } from 'node:child_process'
|
|
9
|
+
import { link } from './link.mjs'
|
|
10
|
+
import { packageManager } from './package-manager.mjs'
|
|
11
|
+
|
|
12
|
+
const repo = process.cwd()
|
|
13
|
+
const { version } = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'))
|
|
14
|
+
const spec = process.argv[3] ?? `@ersbeth/codelife@${version}`
|
|
15
|
+
const pm = packageManager(repo) || process.env.npm_config_user_agent?.split('/')[0] || 'npm'
|
|
16
|
+
const add = pm === 'npm' ? ['install', '-D'] : ['add', '-D']
|
|
17
|
+
|
|
18
|
+
const r = spawnSync(pm, [...add, spec], { cwd: repo, stdio: 'inherit' })
|
|
19
|
+
if (r.status !== 0) process.exit(r.status ?? 1)
|
|
20
|
+
link(repo)
|
|
21
|
+
console.log('codelife: installed. Open Claude Code in this folder, trust it, run /reload-plugins, then /setup.')
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Wires the clone it runs in: the hooks path, the pruning fetch and the ssh keepalive, the three
|
|
3
|
+
// local keys a hook needs to be run and to hold, and the plugin's link (link.mjs). A repository's `prepare` runs it —
|
|
4
|
+
// `node node_modules/@ersbeth/codelife/scripts/install.mjs` — and never its `postinstall`, which
|
|
5
|
+
// npm runs in whoever installs a dependency (R-272). This repository, which is the package, runs
|
|
6
|
+
// `node scripts/install.mjs`.
|
|
7
|
+
//
|
|
8
|
+
// The keepalive is for the one window the `pre-push` hook cannot leave: git runs it after it has
|
|
9
|
+
// contacted the remote, so the tier runs inside a connection held idle, and a remote that drops
|
|
10
|
+
// idle connections breaks the push of a tip the tier just passed. Inert for an HTTPS remote.
|
|
11
|
+
import './node-floor.mjs'
|
|
12
|
+
import { spawnSync } from 'node:child_process'
|
|
13
|
+
import { LINK, link } from './link.mjs'
|
|
14
|
+
|
|
15
|
+
// The keepalive is appended once to a command the contributor set, so an identity for a second
|
|
16
|
+
// account keeps its options (#1059).
|
|
17
|
+
const ssh = spawnSync('git', ['config', 'core.sshCommand'], { encoding: 'utf8' }).stdout?.trim() || 'ssh'
|
|
18
|
+
const keys = [
|
|
19
|
+
['core.hooksPath', '.githooks'],
|
|
20
|
+
['fetch.prune', 'true'],
|
|
21
|
+
['core.sshCommand', ssh.includes('ServerAliveInterval') ? ssh : `${ssh} -o ServerAliveInterval=20 -o ServerAliveCountMax=10`],
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
// Outside a git repository — a tarball unpacked, a build container — there is no clone to wire.
|
|
25
|
+
const clone = spawnSync('git', ['rev-parse', '--git-dir'], { stdio: 'ignore' }).status === 0
|
|
26
|
+
|
|
27
|
+
for (const [key, value] of clone ? keys : []) {
|
|
28
|
+
const r = spawnSync('git', ['config', '--local', key, value], { stdio: 'inherit' })
|
|
29
|
+
if (r.status !== 0) process.exit(r.status ?? 1)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (link(process.cwd())) console.log(`codelife: linked ${LINK} — a Claude Code session already open here, or the one in which you trust this folder, loads its skills only after /reload-plugins.`)
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Lens A11Y — Accessibility & i18n
|
|
2
|
+
|
|
3
|
+
## Objective
|
|
4
|
+
|
|
5
|
+
Say whether the interface can be used by someone who is not the person who wrote it: reachable
|
|
6
|
+
without a mouse, readable by assistive technology, and translatable without editing the markup.
|
|
7
|
+
This lens measures what the **tree** shows; what only a rendered page shows is out of its reach by
|
|
8
|
+
construction, and it says so rather than guessing.
|
|
9
|
+
|
|
10
|
+
## What this lens can and cannot see
|
|
11
|
+
|
|
12
|
+
A lens runs in an agent that only reads. So this lens reads markup, stylesheets, component
|
|
13
|
+
files and resource bundles, and it reports on what those declare. **Focus order as a user meets it,
|
|
14
|
+
a live accessibility tree, a contrast ratio as composited, a screen reader's actual announcement** —
|
|
15
|
+
none of those are visible from a file, and none is estimated here. They belong to a real browser,
|
|
16
|
+
and a finding about them written from the tree alone would
|
|
17
|
+
be a guess with an `Evidence:` line.
|
|
18
|
+
|
|
19
|
+
That boundary is the lens's, not a limitation to apologise for: everything below is checkable from
|
|
20
|
+
the tree, and a repository that fixes it all still needs someone to look at the page.
|
|
21
|
+
|
|
22
|
+
## Questions
|
|
23
|
+
|
|
24
|
+
- **Is every interactive element reachable and named?** A control built from a non-interactive
|
|
25
|
+
element with a click handler and no role, no tab position and no accessible name is invisible to
|
|
26
|
+
everything but a mouse.
|
|
27
|
+
- **Does every image, icon and media element carry a text alternative** — or an explicit marker
|
|
28
|
+
saying it is decorative? An absent alternative and a deliberate empty one are different answers,
|
|
29
|
+
and only one of them is a defect.
|
|
30
|
+
- **Is every form control associated with a label**, by reference and not by proximity?
|
|
31
|
+
- **Is the document's language declared**, and its direction where the supported locales need one?
|
|
32
|
+
- **Are user-visible strings externalised**, or written into the markup? A literal string is a
|
|
33
|
+
string no translator will ever see, and the count of them is the real state of the i18n effort.
|
|
34
|
+
- **Are the locale-sensitive values formatted through the platform's own facilities** — dates,
|
|
35
|
+
numbers, currencies, plurals — or assembled by concatenation? Concatenation is what makes a
|
|
36
|
+
translation grammatically impossible rather than merely absent.
|
|
37
|
+
- **Does anything check any of this automatically**, at any point? Where nothing does, that is the
|
|
38
|
+
finding, and it outlives every individual instance below it.
|
|
39
|
+
|
|
40
|
+
## Probes
|
|
41
|
+
|
|
42
|
+
The subject is the markup among the tracked files: the extensions this
|
|
43
|
+
repository uses — `.html`, `.htm`, `.vue`, `.svelte`, `.jsx`, `.tsx`, `.astro` — read from that list
|
|
44
|
+
and named in *Method* rather than assumed from a framework. None → this lens has no subject: say so
|
|
45
|
+
under `Limits`. Then, searched over that markup:
|
|
46
|
+
|
|
47
|
+
- **Images and media without a text alternative** — `<(img|area|input[^>]*type="image")[^>]*>`,
|
|
48
|
+
each hit counted as named (`alt="…"`), explicitly decorative (`alt=""`) or without one.
|
|
49
|
+
- **Controls a keyboard cannot reach** — `<(div|span|li|td)[^>]*(onClick|@click|v-on:click|on:click)`,
|
|
50
|
+
the hits carrying neither `role=` nor `tabindex=`.
|
|
51
|
+
- **Form controls, against the labels that reference them** — `<(input|select|textarea)\b` counted
|
|
52
|
+
beside `<label[^>]*\b(for|:for|htmlFor)=`.
|
|
53
|
+
- **The declared language and direction** — `<html[^>]*>`, and `\b(lang|dir)="[^"]*"` by value.
|
|
54
|
+
- **Externalised strings against literal ones** — the tracked paths under a `locales`, `locale`,
|
|
55
|
+
`i18n`, `lang` or `translations` directory, beside the user-visible text still in the markup,
|
|
56
|
+
`>[[:space:]]*[A-Z][a-z]+( [a-z]+){2,}`.
|
|
57
|
+
- **Locale-sensitive formatting, done by the platform or by hand** — over every tracked file,
|
|
58
|
+
`\bIntl\.[A-Za-z]+` beside `toFixed\(|toLocaleString\(\)|' \+ .*(count|total|name)`.
|
|
59
|
+
- **Whether anything checks this without a person** — `axe|pa11y|lighthouse|eslint-plugin-jsx-a11y|vue-a11y|accessibility`
|
|
60
|
+
in `package.json`; nothing → nothing checks this but a reader.
|
|
61
|
+
|
|
62
|
+
Every count above is reported with the command beside it, and a count of zero from an empty markup
|
|
63
|
+
subject is *not applicable*, never a pass.
|
|
64
|
+
|
|
65
|
+
## For a change
|
|
66
|
+
|
|
67
|
+
A change gives every interactive element it adds a name, every image an alternative, and writes
|
|
68
|
+
no user-visible string into the markup — each checked by the probes above, run over the files it
|
|
69
|
+
touches.
|
|
70
|
+
|
|
71
|
+
The i18n objective is the one worth being strict about: a literal string is trivial to externalise
|
|
72
|
+
while the line is being written and expensive once a page is full of them, so a change adds none
|
|
73
|
+
even where the repository holds many already, whose count is a campaign's.
|
|
74
|
+
|
|
75
|
+
What a change does to focus order, contrast or a screen reader's announcement no command checks
|
|
76
|
+
here — the probes see no live accessibility tree — so an objective on it is left to the review.
|
|
77
|
+
|
|
78
|
+
## Signals learned
|
|
79
|
+
|
|
80
|
+
- **This lens ships without campaign history**, no application having been ported when it was
|
|
81
|
+
written, and its content was settled at what a subagent can read rather than deferred whole. Grow it
|
|
82
|
+
from the first application campaign.
|
|
83
|
+
- **The absence of automated checking outranks the instances.** Fifty missing alternatives are one
|
|
84
|
+
cause; report the cause as a finding and the instances as its evidence, rather than fifty findings
|
|
85
|
+
a reader has to group themselves.
|
|
86
|
+
- **An empty alternative is an answer.** A probe that counts *no `alt`* and *`alt=""`* together
|
|
87
|
+
reports deliberate decisions as defects and loses the reader's trust for the rest of the report.
|
|
88
|
+
- **Count the literal strings.** It is the one i18n figure that is unarguable, cheap, and moves in a
|
|
89
|
+
direction anyone can see between two campaigns.
|
|
90
|
+
- Do not report an ARIA attribute as an improvement where a native element would do. Added ARIA on
|
|
91
|
+
a wrong element is a second wrong answer, and this lens should not recommend one.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Lens ARCH — Architecture & design
|
|
2
|
+
|
|
3
|
+
## Objective
|
|
4
|
+
|
|
5
|
+
Say whether the public surface can be enumerated, whether dependencies go one way, and whether a
|
|
6
|
+
path predicts what it holds. A layered design whose layers call each other in both directions is
|
|
7
|
+
one layer with extra files, and nobody finds out until a change has to be made in five places at
|
|
8
|
+
once; a layout whose paths lie sends every reader to the wrong file first.
|
|
9
|
+
|
|
10
|
+
## Questions
|
|
11
|
+
|
|
12
|
+
- **Can the public surface be listed** from the repository itself — the commands, the exported
|
|
13
|
+
symbols, the entry points — or does it take reading every file to know what a consumer can touch?
|
|
14
|
+
- **Do dependencies go one way?** Name the layers the repository claims, then check the direction at
|
|
15
|
+
the boundary. The interesting failure is not a cycle in the imports; it is a lower layer that
|
|
16
|
+
*knows* about a higher one — a core module that imports the UI, a utility that reaches back into
|
|
17
|
+
the feature that uses it.
|
|
18
|
+
- Is the **boundary written down** where the code is, or only in a design document? A rule that
|
|
19
|
+
lives only in `docs/` is a rule the next contributor will not meet.
|
|
20
|
+
- Does the repository declare **more surface than it has**, or less? A manifest listing entry points
|
|
21
|
+
that no longer exist, or omitting ones consumers already use.
|
|
22
|
+
- What would it take to **remove one piece**? A component nothing can be removed around is a
|
|
23
|
+
component everything depends on.
|
|
24
|
+
- **Does a path predict the domain it holds?** Given a feature and no search, can a contributor say
|
|
25
|
+
where its file lives, and be right? What is in `utils/`, `helpers/`, `misc/`, `common/` — names
|
|
26
|
+
that mean *we had nowhere to put this*, whose contents are the domains the layout does not have?
|
|
27
|
+
- **Does a path predict the status of what it holds** — shipped, generated, test, fixture? The
|
|
28
|
+
dangerous case is generated output beside hand-written source with nothing distinguishing them.
|
|
29
|
+
Are there **two homes for one kind of thing** — tests in two trees, configuration in three places?
|
|
30
|
+
What the target's layout imposes is CONF's question; what the repository's own paths say is this
|
|
31
|
+
one.
|
|
32
|
+
|
|
33
|
+
## Probes
|
|
34
|
+
|
|
35
|
+
- **The boundary this repository declares** — the kind of artefact it ships most, as a glob over the
|
|
36
|
+
tracked files, and the marker that puts an instance on one side of it or the other,
|
|
37
|
+
both read from the tree and named in *Method*. The instances the marker is found in are one side,
|
|
38
|
+
the rest of the kind the other. No instance of the kind → the boundary probe has no subject, said
|
|
39
|
+
under `Limits`.
|
|
40
|
+
- **Is the boundary written where the code is?** Where an artefact declares what it depends on,
|
|
41
|
+
read that declaration beside the call sites in its own files, and compare the two both ways.
|
|
42
|
+
- **The surface, against what the manifest declares** — the manifest is `release.version_file` of
|
|
43
|
+
the contract the caller passed you, never a literal path, the same reason GOV reads it from the
|
|
44
|
+
contract: its top-level keys are the declared surface, and a manifest that is not JSON leaves the
|
|
45
|
+
surface unread, said under `Limits`.
|
|
46
|
+
- **The shape of the tree, at the depth where domains live** — the directories of the tracked
|
|
47
|
+
files' paths, three levels down.
|
|
48
|
+
- **The names that mean "nowhere else to put this"** — tracked paths through a `utils`, `helpers`,
|
|
49
|
+
`misc` or `common` directory.
|
|
50
|
+
- **Does status show in the path?** Generated output beside source is the case that bites: tracked
|
|
51
|
+
paths under `dist/`, `build/` or `generated/`.
|
|
52
|
+
|
|
53
|
+
**A grep for the other side's *name* is not this probe.** A skill's files name other skills without
|
|
54
|
+
invoking them, and two shapes do it in every corpus: the skill's own `## Calls` section, which is a
|
|
55
|
+
declaration and not a call site, and guidance naming a skill as *Next* — `/setup` here. Match
|
|
56
|
+
on the **invocation**, and read a skill's declaration apart from its call sites, or the lens
|
|
57
|
+
reports a violation per document.
|
|
58
|
+
|
|
59
|
+
## For a change
|
|
60
|
+
|
|
61
|
+
A change adds no call in the forbidden direction, no surface the manifest does not declare, no file
|
|
62
|
+
where its path does not predict it, and no second home for something that already has one — each
|
|
63
|
+
answerable from the files it touches and their neighbours. The layer and tree inventories are a
|
|
64
|
+
campaign's, and no objective rests on them.
|
|
65
|
+
|
|
66
|
+
## Signals learned
|
|
67
|
+
|
|
68
|
+
- **The direction rule needs a mechanical form, or it is prose.** codelife's own is each skill's
|
|
69
|
+
declared call list, held equal both ways to the call sites found in its own files by a guard in
|
|
70
|
+
the suite. Frontmatter cannot carry such a rule, which is why it is a guard and not a field.
|
|
71
|
+
- **What the probe above looked like on codelife**, as a worked example of filling its two
|
|
72
|
+
parameters: `kind='*/SKILL.md'` and `marker='^user-invocable: false'` answered the menu boundary
|
|
73
|
+
both ways, and the declaration-against-call-sites question was answered by reading each skill's
|
|
74
|
+
`## Calls` section apart from the `<name>.<operation>` matches in its own files. Those values are
|
|
75
|
+
codelife's; the shape is every repository's.
|
|
76
|
+
- A repository whose layers are **directories** makes this lens cheap; one whose layers are a
|
|
77
|
+
convention in file names makes it a judgement call, and the lens says which it was.
|
|
78
|
+
- A manifest with five keys and a surface of seven commands is not a defect on its own — say what
|
|
79
|
+
the manifest is *for* in that ecosystem before calling the gap a finding.
|
|
80
|
+
- **A repository with no `utils/` is worth saying so.** Measured on codelife: none of `utils`,
|
|
81
|
+
`helpers`, `misc` or `common` existed, and the layout was `skills/<name>/{SKILL.md,references/,
|
|
82
|
+
templates/}` — the same three names everywhere. That is the answer, and it is short.
|
|
83
|
+
- **The status question is where a mostly-prose repository still has teeth.** codelife's
|
|
84
|
+
`CHANGELOG.md` is generated and sits at the root beside hand-written files; what distinguishes it
|
|
85
|
+
is a `generated[]` entry in the contract, not its path. Say when the distinction lives somewhere
|
|
86
|
+
other than the layout.
|
|
87
|
+
- A `templates/` directory beside a `references/` directory is a real distinction — one is copied
|
|
88
|
+
into a target repository, the other is read by the skill — and it is invisible from the names
|
|
89
|
+
alone. When a layout encodes a distinction only its author knows, that is the finding.
|
|
90
|
+
- Depth is not a defect. Do not report nesting; report a path that lies.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Lens BUG — Defects
|
|
2
|
+
|
|
3
|
+
## Objective
|
|
4
|
+
|
|
5
|
+
Say what the code gets wrong — its logic, its states, its asynchrony, its resources — on the
|
|
6
|
+
boundaries a caller can reach. Not whether a test covers it: a defect no test reaches is still a
|
|
7
|
+
defect, and the one this lens exists to find.
|
|
8
|
+
|
|
9
|
+
## Questions
|
|
10
|
+
|
|
11
|
+
- **Trace each changed or central function branch by branch.** For each branch, what input reaches
|
|
12
|
+
it, and is the result right there? The empty collection, the single element, the last page, the
|
|
13
|
+
boundary value, the value that is neither — `0`, `''`, `null`, `NaN`.
|
|
14
|
+
- **What states can the code be in, and is each handled?** A transition that leaves an object half
|
|
15
|
+
updated, a flag two paths set and one path clears, a state the type allows and no branch expects.
|
|
16
|
+
- **What does asynchrony allow?** Two calls interleaved, a result arriving after the caller moved
|
|
17
|
+
on, a read and a write of the same value with an `await` between them.
|
|
18
|
+
- **Is every resource released** on every path — a file, a handle, a listener, a timer, a lock —
|
|
19
|
+
including the path that throws?
|
|
20
|
+
- **What did a deleted line carry that nothing restored?** A guard, a normalisation, a default, an
|
|
21
|
+
ordering. Removed lines are read as hard as added ones.
|
|
22
|
+
- Does a value cross a **unit, encoding or index base** without saying so — milliseconds and
|
|
23
|
+
seconds, bytes and characters, one-based and zero-based?
|
|
24
|
+
|
|
25
|
+
## Probes
|
|
26
|
+
|
|
27
|
+
- **The files the tree changes most** — where defects concentrate, and where to trace first: the
|
|
28
|
+
history `verify` handed over under `tree`.
|
|
29
|
+
- **Comparisons that turn an empty value into a syntax error, in the shell scripts the tree ships**
|
|
30
|
+
— the tracked files whose first line is a shell shebang, `^#!.*sh`, then `\[ \$[a-z_]+ [=!]` in
|
|
31
|
+
them.
|
|
32
|
+
- **Fixes already made** — the fix commits of that history: a defect class this repository has had
|
|
33
|
+
is worth looking for again beside it.
|
|
34
|
+
|
|
35
|
+
The probes only point at where to read: the method is the trace. Name in *Method* which functions
|
|
36
|
+
were traced and to which depth — a lens that read three functions and says so is worth more than
|
|
37
|
+
one that claims the tree.
|
|
38
|
+
|
|
39
|
+
## For a change
|
|
40
|
+
|
|
41
|
+
A change holds on every branch it adds or changes, and on the boundaries a caller reaches through
|
|
42
|
+
them — the empty case, the last case, the concurrent case — each named with the test that reaches
|
|
43
|
+
it. What a line it removes carried — a check, a default, an ordering — is restored, or the plan
|
|
44
|
+
says why it is no longer owed. A defect the change makes reachable on a line it leaves untouched is
|
|
45
|
+
the change's own, and its objective names that line.
|
|
46
|
+
|
|
47
|
+
## Signals learned
|
|
48
|
+
|
|
49
|
+
- A repository that is mostly prose still has a defect surface: its scripts, its hooks, its
|
|
50
|
+
generators. Say which files carried the lens rather than reporting *not applicable*.
|
|
51
|
+
- An unquoted variable in a shell test — `[ $x = y ]` — is a defect that shows only on the empty
|
|
52
|
+
value, which is exactly the value nobody tries by hand.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Lens BUILD — Build
|
|
2
|
+
|
|
3
|
+
## Objective
|
|
4
|
+
|
|
5
|
+
Say whether the build is reproducible, silent, and whether what is committed as its output
|
|
6
|
+
matches what the sources produce today. A stale committed artefact is a lie that ships.
|
|
7
|
+
|
|
8
|
+
## Questions
|
|
9
|
+
|
|
10
|
+
Whether the build runs, is silent, leaves the committed output as it is and gives the same bytes
|
|
11
|
+
twice is the script's to say; what it reports is read here for its cause and its weight — a
|
|
12
|
+
difference in committed output weighs as far as the two have drifted, a non-reproducible file as
|
|
13
|
+
what bakes it (a timestamp, an absolute path).
|
|
14
|
+
|
|
15
|
+
- Does the build **run clean from a fresh clone**, with the declared package manager and the
|
|
16
|
+
declared version, without a manual step nobody wrote down?
|
|
17
|
+
- Does the build **depend on something undeclared** — a global binary, an environment variable, a
|
|
18
|
+
network fetch at build time?
|
|
19
|
+
|
|
20
|
+
## Probes
|
|
21
|
+
|
|
22
|
+
`BUILD.mjs`, run by `verify` before the lenses, whose lines you are handed. It builds
|
|
23
|
+
twice in the tree with the manager the lockfile names — say so in *Method*, and let the caller's
|
|
24
|
+
closing fingerprint report what it left — and reports a build that fails, each line of its output
|
|
25
|
+
that warns, each tracked file the build changed, and each file two builds wrote differently. With
|
|
26
|
+
no `build` script it could not tell, which is *not applicable* under *Limits*.
|
|
27
|
+
|
|
28
|
+
## For a change
|
|
29
|
+
|
|
30
|
+
A change to a source a `generated[]` entry builds from updates that output with it. The contract
|
|
31
|
+
declares which paths are built from which sources, so the objective names the output and the
|
|
32
|
+
command that regenerates it with nothing left to commit.
|
|
33
|
+
|
|
34
|
+
## Signals learned
|
|
35
|
+
|
|
36
|
+
- A repository with no build at all is not a BUILD finding — the lens reports *not applicable* and
|
|
37
|
+
says so in *Limits*. Inventing a build to audit is how an audit loses a reader.
|
|
38
|
+
- `dist/` committed **and** in `.gitignore` is a real state and a real finding: the file is
|
|
39
|
+
tracked from before it was ignored, and git will keep serving the stale one.
|
|
40
|
+
- A build that is silent only because its warnings are routed to a log file is not silent.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Lens COMPAT — Runtime & browser compatibility
|
|
2
|
+
|
|
3
|
+
## Objective
|
|
4
|
+
|
|
5
|
+
Say which runtimes and browsers the application claims to support, where that claim is written, and
|
|
6
|
+
whether anything tests it there. A support claim nobody derives the build from is a claim the first
|
|
7
|
+
user on the oldest supported browser disproves.
|
|
8
|
+
|
|
9
|
+
## Questions
|
|
10
|
+
|
|
11
|
+
- **What is the declared target?** The runtime floor in the manifest, the browser list wherever the
|
|
12
|
+
repository keeps it, the target the build tool is configured with — and do those three agree? They
|
|
13
|
+
are three declarations of one fact, and they drift apart silently because each is read by a
|
|
14
|
+
different tool.
|
|
15
|
+
- **Does the shipped output respect the declared target?** Syntax the target cannot parse is a blank
|
|
16
|
+
page, not a degraded feature, and it is produced by a build whose own configuration said otherwise.
|
|
17
|
+
- **Is anything tested on the target**, or only on whatever the contributor and the pipeline happen
|
|
18
|
+
to run? One runtime version passing is evidence about one runtime version.
|
|
19
|
+
- **Which platform APIs does the code call**, and is each one available across the declared range?
|
|
20
|
+
The dangerous ones are recent and widely shipped — available everywhere the author looked.
|
|
21
|
+
- **What happens on an unsupported runtime** — a message, a degraded mode, or a stack trace?
|
|
22
|
+
- **Is the declared range still the intended one?** A browser list copied from a template years ago
|
|
23
|
+
is a cost paid at every build for users who no longer exist.
|
|
24
|
+
|
|
25
|
+
## Probes
|
|
26
|
+
|
|
27
|
+
- **The three declarations, wherever this repository keeps them** — `engines` and `browserslist`
|
|
28
|
+
in `package.json`, each absence reported as an absence rather than as a default; and the tracked
|
|
29
|
+
runtime declaration files — `.browserslistrc`, `.nvmrc`, `.node-version`, `.tool-versions` — or
|
|
30
|
+
the line saying there is none.
|
|
31
|
+
- **The target the build is configured with** — `target|browserslist|esbuild|transpile|downlevel`
|
|
32
|
+
in the configuration the repository's build tool reads, found in the tree and named in *Method*;
|
|
33
|
+
none found → the target is undeclared on this side.
|
|
34
|
+
- **What the shipped output actually requires** — `\?\?=|\?\.|#[a-zA-Z]+ *[=(]|static *\{|await `
|
|
35
|
+
over the built artefacts the contract's `generated[]` rows name, the newest syntax deciding the
|
|
36
|
+
real floor; no built output → the syntax floor is unmeasured, not zero.
|
|
37
|
+
- **Where anything is tested, and on what** — the tracked CI and container files — `.gitlab-ci.yml`,
|
|
38
|
+
`.github/workflows/`, a `Dockerfile` — and `(node|browser|playwright|chromium|firefox|webkit)[-_ ]?version[^,}]*`
|
|
39
|
+
across the tree.
|
|
40
|
+
- **Platform APIs the code reaches for**, which is what a declared range has to cover —
|
|
41
|
+
`\b(navigator|structuredClone|ResizeObserver|IntersectionObserver|AbortSignal|Intl|OffscreenCanvas|WebGL2RenderingContext|showOpenFilePicker)\b`,
|
|
42
|
+
counted by name.
|
|
43
|
+
|
|
44
|
+
A repository with no declared target and no build answers *not applicable* under `Limits`: this lens
|
|
45
|
+
measures the distance between a claim and its verification, and where there is no claim the finding
|
|
46
|
+
is the absence of one.
|
|
47
|
+
|
|
48
|
+
## For a change
|
|
49
|
+
|
|
50
|
+
A change keeps the declared target unless it moves it on purpose, and reaches past it nowhere. The
|
|
51
|
+
second is the one easily missed: an added call to a platform API newer than the declared floor
|
|
52
|
+
looks like ordinary code and breaks only on the oldest browser the repository promises to support.
|
|
53
|
+
Where the change moves the build's target, the plan names what the move makes unreachable.
|
|
54
|
+
|
|
55
|
+
## Signals learned
|
|
56
|
+
|
|
57
|
+
- **This lens ships without campaign history**, no application having been ported when it was
|
|
58
|
+
written. Grow it from the first application campaign.
|
|
59
|
+
- **Three declarations, one fact.** The runtime floor, the browser list and the build target are
|
|
60
|
+
read by three different tools and nothing compares them; where two disagree, report which one the
|
|
61
|
+
shipped artefact actually obeys — that is the one users meet.
|
|
62
|
+
- **Measure the output, not the intention.** The syntax in the built artefact is the real floor,
|
|
63
|
+
whatever the configuration says, and it is the only figure here that cannot be argued with.
|
|
64
|
+
- **A pipeline matrix is a support claim.** Where one exists and omits a declared target, that gap is
|
|
65
|
+
a finding with its evidence already written down.
|
|
66
|
+
- Do not report a missing browser list as a defect for a repository that ships no browser code. Ask
|
|
67
|
+
what is claimed before asking what is tested.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Lens CONF — Conformity to the target
|
|
2
|
+
|
|
3
|
+
## Objective
|
|
4
|
+
|
|
5
|
+
Say where the repository differs from the target definition. The target lives in the plugin and
|
|
6
|
+
never in the repository, so a difference is drift rather than a local convention, and the question
|
|
7
|
+
is never *which shape is better* but *how far is this one from the one definition every repository
|
|
8
|
+
converges on*.
|
|
9
|
+
|
|
10
|
+
## Referential
|
|
11
|
+
|
|
12
|
+
**It is handed to you; you never invent it**: the target definition, `target-base.md` and
|
|
13
|
+
`target-<kind>.md`, and `measure.txt`, the port's own measure of each item, copied into your
|
|
14
|
+
folder. One of them missing → say so under `Limits` and emit nothing. A lens that invents a
|
|
15
|
+
target measures the repository against itself, which is the one thing this lens exists not to do.
|
|
16
|
+
|
|
17
|
+
## Questions
|
|
18
|
+
|
|
19
|
+
- **Which items does `measure.txt` read *to do*?** Each is a candidate, its remedy
|
|
20
|
+
`/port-toolchain`; you never classify an item again. One it reads deferred, refused or blocked is
|
|
21
|
+
a line of your report, one *not read* a line of `Limits`, and a `toolchain: not ported` gives no
|
|
22
|
+
candidate at all — a deliberate stop is not an unfinished port.
|
|
23
|
+
- For an item it reads *to do*, is anything in the tree **a local reinvention of what the target
|
|
24
|
+
defines** for it? A second hook, a second script name, a second layout for the same job.
|
|
25
|
+
|
|
26
|
+
## Probes
|
|
27
|
+
|
|
28
|
+
Read each item `measure.txt` reads *to do* against its rows of the definition: the file, script,
|
|
29
|
+
hook or setting they name, searched in `tracked.txt` and read where it is.
|
|
30
|
+
|
|
31
|
+
## For a change
|
|
32
|
+
|
|
33
|
+
A change moves no item of the target's definition away from it: an item it touches, done before
|
|
34
|
+
the change, is not *to do* after it — no script renamed off the standard name, no
|
|
35
|
+
hook edited into a local variant, no generated output the target declares that the change stops
|
|
36
|
+
producing. The tree's conformity is a campaign's, never a change's.
|
|
37
|
+
|
|
38
|
+
A change that touches nothing the definition names has no objective here. That is the common case,
|
|
39
|
+
and padding it is what makes the lens ignorable.
|
|
40
|
+
|
|
41
|
+
## Signals learned
|
|
42
|
+
|
|
43
|
+
- **This lens ships without campaign history**, having been written while the definition it reads
|
|
44
|
+
covered the base and one `kind`. Grow it from the first campaign that runs it.
|
|
45
|
+
- **Drift is usually one aspect, not one item.** A repository that renamed one standard script has
|
|
46
|
+
usually renamed the hook that calls it and the documentation that names it; look for the aspect
|
|
47
|
+
behind two item-level differences before writing them as two findings.
|
|
48
|
+
- Do not report a divergence the target does not cover. A framework, a router, a CSS approach, a
|
|
49
|
+
deployment — those are per-repository choices, and the target says so.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Lens DEPS — Dependencies & supply chain
|
|
2
|
+
|
|
3
|
+
## Objective
|
|
4
|
+
|
|
5
|
+
Say which dependencies fix a horizon, whether the lockfile matches the manager, and what the
|
|
6
|
+
repository needs that it never declares. The dangerous dependency is rarely the one in the manifest;
|
|
7
|
+
it is the binary everyone happens to have installed.
|
|
8
|
+
|
|
9
|
+
## Questions
|
|
10
|
+
|
|
11
|
+
Whether one lockfile, the manager's own, is kept, and whether `engines` and `packageManager` pin
|
|
12
|
+
the runtime, is the script's to say; an unpinned manager is a different resolver on every machine.
|
|
13
|
+
|
|
14
|
+
- **What does the repository call that it does not declare?** External binaries invoked by scripts,
|
|
15
|
+
hooks and CI — read them in the tracked scripts, the hooks and `package.json`. These are
|
|
16
|
+
dependencies with no version, no lockfile and no install step — and they fail on a colleague's
|
|
17
|
+
machine, not on the author's.
|
|
18
|
+
- Which dependency **fixes a horizon**: a peer range that decides what a consumer may install, a
|
|
19
|
+
transitive pin that blocks an upgrade elsewhere?
|
|
20
|
+
- Anything **unmaintained, deprecated, or resolving to a different registry** than the others?
|
|
21
|
+
|
|
22
|
+
## Probes
|
|
23
|
+
|
|
24
|
+
`DEPS.mjs`, run by `verify` before the lenses, whose lines you are handed. It reports
|
|
25
|
+
several lockfiles, a lockfile of another manager than `packageManager`, no `engines`, and no
|
|
26
|
+
`packageManager`. With no `package.json` it could not tell.
|
|
27
|
+
|
|
28
|
+
## For a change
|
|
29
|
+
|
|
30
|
+
A dependency a change adds, a version it moves and **a binary it invokes** are each declared where
|
|
31
|
+
the repository declares its dependencies. The third is the one easily missed, because it looks
|
|
32
|
+
like a shell line rather than a dependency change.
|
|
33
|
+
|
|
34
|
+
## Signals learned
|
|
35
|
+
|
|
36
|
+
- **Zero declared dependencies does not mean no supply chain.** Report the undeclared set as the
|
|
37
|
+
dependency list, because that is what it is.
|
|
38
|
+
- **`engines` and `packageManager` both absent** is worth one line even when nothing has broken:
|
|
39
|
+
a hook that calls a manager by name already assumes one the repository never names.
|
|
40
|
+
- A tool only the tracker's verbs call — `glab`, `gh` — is a different class from one a hook or a
|
|
41
|
+
build calls: the first stops a verb saying *not connected*, the second breaks a commit. Say which.
|
|
42
|
+
- Do not report the absence of a lockfile as a finding when there is nothing to lock. Report the gap
|
|
43
|
+
between what is locked and what is called.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Lens DOC — Documentation
|
|
2
|
+
|
|
3
|
+
## Objective
|
|
4
|
+
|
|
5
|
+
Say whether every verifiable statement in the documentation is true **today**, and what would
|
|
6
|
+
remind a committer to update it tomorrow. Documentation is not audited for beauty or completeness:
|
|
7
|
+
it is audited for truth and for the mechanism that keeps it true.
|
|
8
|
+
|
|
9
|
+
## Questions
|
|
10
|
+
|
|
11
|
+
- **The authority document first.** The `CLAUDE.md`, or whatever file an agent reads first and
|
|
12
|
+
trusts most: a false line there costs more than the same line anywhere else, because nothing
|
|
13
|
+
downstream questions it. Every verifiable statement in it is checked before any other document's.
|
|
14
|
+
- **Is there exactly one authority?** `CLAUDE.md`, `AGENTS.md`, `.cursorrules`, a copilot
|
|
15
|
+
instructions file — several is worse than none, because an agent picks one and never learns the
|
|
16
|
+
others contradict it.
|
|
17
|
+
- **Is its transient part mixed with its standing rules?** A brief whose rules and whose current
|
|
18
|
+
state are interleaved rots as one document, and the rules rot with the state.
|
|
19
|
+
- **Which statements are verifiable, and are they true?** A command, a path, a version, a script
|
|
20
|
+
name, a URL, a count. Resolve them against the tree, count them. A prose claim about intent is not this
|
|
21
|
+
lens's business; a command that no longer exists is. Whether the code does what a document
|
|
22
|
+
*specifies* is INTENT's question, not this one.
|
|
23
|
+
- Is a **fact stated in two places**? Duplication of a rule is acceptable; duplication of a
|
|
24
|
+
verifiable fact means one of the two is already wrong or soon will be.
|
|
25
|
+
- Does the documentation say what the repository **does now**, or what it did? Look for the
|
|
26
|
+
tell-tales: a *Status* line, a *Next steps* section, a roadmap with dates.
|
|
27
|
+
- Is there a **mechanism** keeping it true — a test that runs the README's commands, a hook, a
|
|
28
|
+
generated section — or does it depend on someone remembering?
|
|
29
|
+
- Are the **audiences** separated? A README that is at once a sales page, a tutorial and a
|
|
30
|
+
contributor guide serves none of the three.
|
|
31
|
+
- Do the **links resolve**, including relative ones after a file moved?
|
|
32
|
+
|
|
33
|
+
## Probes
|
|
34
|
+
|
|
35
|
+
- **One authority, or several — and how much of it is transient** — which of `CLAUDE.md`,
|
|
36
|
+
`AGENTS.md`, `.cursorrules` and `.github/copilot-instructions.md` the tracked files hold, and
|
|
37
|
+
their `^## ` headings.
|
|
38
|
+
- **Every fenced command in the documentation** — the `bash`, `sh` and `console` fences of
|
|
39
|
+
`README.md`, `CONTRIBUTING.md` and `docs/`, each read with its lines, to check it.
|
|
40
|
+
- **Script names cited in prose against the ones that exist** — `pnpm [a-z:]*|npm run [a-z:]*` in
|
|
41
|
+
the Markdown, beside the `scripts` of `package.json`.
|
|
42
|
+
- **Relative links that point nowhere** — `\]\([^)#][^)]*\.md[^)]*\)` in the Markdown, each target,
|
|
43
|
+
its anchor dropped, looked for among the tracked files.
|
|
44
|
+
- **When the documentation last moved against when the code did** — the two dates of the history
|
|
45
|
+
`verify` handed over under `tree`: both sides are the tree's own, not a layout this or that
|
|
46
|
+
repository happens to have.
|
|
47
|
+
|
|
48
|
+
## For a change
|
|
49
|
+
|
|
50
|
+
A change to something the documentation states changes the documentation with it. **No Markdown
|
|
51
|
+
line still carries what the change removes**: each name, path and command it takes away and does
|
|
52
|
+
not carry elsewhere, searched in the repository's Markdown as a whole word and a fixed string,
|
|
53
|
+
finds nothing. A behaviour that changes without any name changing is INTENT's, against the issue.
|
|
54
|
+
|
|
55
|
+
And a change adds no verifiable statement — a command, a path — that is already false when
|
|
56
|
+
written: the cheaper fix, and the more embarrassing one to ship.
|
|
57
|
+
|
|
58
|
+
## Signals learned
|
|
59
|
+
|
|
60
|
+
- A *Status* or *Where we are* section is the fastest-rotting prose in any repository: check it
|
|
61
|
+
first, and check every claim in it individually. Found on codelife, where the brief still said a
|
|
62
|
+
tool was not installed hours after it had been.
|
|
63
|
+
- A command in a README that was correct when written and is now wrong will have been copied into
|
|
64
|
+
a CI file, an issue template and a colleague's notes. Search for it before declaring it fixed in
|
|
65
|
+
one place.
|
|
66
|
+
- Documentation duplicating a *verifiable fact* the tooling holds — a script's body, a command, a
|
|
67
|
+
path — is a finding even when both copies currently agree.
|
|
68
|
+
- **A brief that is mostly state is a brief whose rules rot with its state.** Measured on codelife:
|
|
69
|
+
120 of 201 lines of its `CLAUDE.md` were a transient *Where we are*. The standing rules above it
|
|
70
|
+
were correct; the question is whether a reader who stops after the first false line ever reaches
|
|
71
|
+
them.
|
|
72
|
+
- One authority document and no competitor is a good answer — say it, briefly, rather than skipping
|
|
73
|
+
to what is wrong.
|