@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,57 @@
|
|
|
1
|
+
# Lens FAIL — Silent failures
|
|
2
|
+
|
|
3
|
+
## Objective
|
|
4
|
+
|
|
5
|
+
Say where a failure stays silent — swallowed, unawaited, hidden behind a fallback, or lost as it
|
|
6
|
+
crosses a boundary. A failure that reports itself is a bug someone will fix; one that returns the
|
|
7
|
+
same value as success is a bug nobody sees until its consequence arrives somewhere else.
|
|
8
|
+
|
|
9
|
+
## Questions
|
|
10
|
+
|
|
11
|
+
- **Does any `catch` swallow?** An empty handler, one that logs and carries on as if nothing
|
|
12
|
+
happened, one that catches everything to handle one case. Whatever file it is in — a build
|
|
13
|
+
script, a test's cleanup, a hook — the failure it hides is this lens's.
|
|
14
|
+
- **Is every promise awaited, every status read?** A call whose rejection nobody observes, an exit
|
|
15
|
+
code nobody checks, a `|| true` that turns a failure into a pass.
|
|
16
|
+
- **Does a fallback hide an error?** A default returned when the real value could not be read is a
|
|
17
|
+
decision; returned without saying so, it is a failure wearing the shape of success.
|
|
18
|
+
- **Does a failure stay a failure as it crosses a boundary** — a subshell, a pipe, a callback, a
|
|
19
|
+
worker, a process? A flag set where nobody reads it is the classic form.
|
|
20
|
+
- Is anything **guarded by a comment instead of by code** — *must be called after init*, *do not
|
|
21
|
+
pass null* — so that breaking it fails nowhere?
|
|
22
|
+
- Are the **known limits written where a user meets them**? A documented limit is a decision; an
|
|
23
|
+
undocumented one is a failure nobody will report.
|
|
24
|
+
|
|
25
|
+
## Probes
|
|
26
|
+
|
|
27
|
+
- **The shell scripts this repository ships** — the tracked files whose first line is a shell
|
|
28
|
+
shebang, `^#!.*sh`: the subject of the next two. None → FAIL's shell probes have no subject, said
|
|
29
|
+
under `Limits` — finding nothing in no script is the wrong answer wearing the shape of "every
|
|
30
|
+
script is armed".
|
|
31
|
+
- **Does every shipped script arm itself against unset variables and failed pipes?** — the scripts
|
|
32
|
+
in which `set -[a-z]*u` is not found.
|
|
33
|
+
- **The failure that does not cross the boundary** — `\| *while` in those scripts: a `while` on the
|
|
34
|
+
right of a pipe runs in a subshell under bash, so a flag set inside it is lost when the loop ends.
|
|
35
|
+
- **Failures turned into passes**, in every tracked file — `\|\| *(true|:)( |$)`.
|
|
36
|
+
- **Handlers that swallow**, in the languages the tree carries —
|
|
37
|
+
`catch *(\([^)]*\))? *\{ *\}|except[^:]*: *pass`.
|
|
38
|
+
|
|
39
|
+
## For a change
|
|
40
|
+
|
|
41
|
+
A change adds no path that can fail without saying so — a `catch` that swallows, a promise not
|
|
42
|
+
awaited, a `|| true`, a fallback that returns a default where it used to throw — and widens no
|
|
43
|
+
`catch` that was narrow. A call it moves across a boundary — into a callback, a subshell, a worker
|
|
44
|
+
— still reports its failure to the caller. A test's setup or cleanup is code like any other: a
|
|
45
|
+
restore that can fail silently is this lens's, not VERIF's.
|
|
46
|
+
|
|
47
|
+
## Signals learned
|
|
48
|
+
|
|
49
|
+
- **`| while` is codelife's own recurring defect**, and a shape worth looking for anywhere.
|
|
50
|
+
Campaign A1 filed it as T-4 — a `ko` setting `fail=1` inside a pipeline subshell, so the script
|
|
51
|
+
printed FAIL and exited 0. It was fixed, and then written again in one of the plugin's own suites
|
|
52
|
+
in the very session that fixed it. Read from a redirect: `done < <(…)`.
|
|
53
|
+
- A variant worth its own look: a pipeline that **swallows its own diagnostics**. The same file
|
|
54
|
+
piped its FAIL lines into `grep -q`, so the check fired correctly and never showed which line
|
|
55
|
+
triggered it. A guard that cannot say *where* costs as much as one that does not fire.
|
|
56
|
+
- `set -euo pipefail` at the top of every script is a good sign and not a conclusion — `-e` does not
|
|
57
|
+
survive a function called in a condition, which is where the interesting failures live.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Lens GOV — Governance & release
|
|
2
|
+
|
|
3
|
+
## Objective
|
|
4
|
+
|
|
5
|
+
Say whether every branch has a status, every version a tag, and the licence a decision. This lens
|
|
6
|
+
looks at what the repository says about itself as a project rather than as code — and its findings
|
|
7
|
+
are usually things nobody owns, which is why they accumulate.
|
|
8
|
+
|
|
9
|
+
## Questions
|
|
10
|
+
|
|
11
|
+
Whether a merged branch is still a head on `origin`, a name falls outside the model, the shipped
|
|
12
|
+
version has no tag, a licence is missing, and this clone keeps refs the forge deleted, is the
|
|
13
|
+
script's to say. A branch list nobody prunes is a list nobody reads, and the one stale branch that
|
|
14
|
+
matters hides in it.
|
|
15
|
+
|
|
16
|
+
- **Does every branch have a status?** Unmerged and untouched for months, or kept for a reason
|
|
17
|
+
nobody wrote down.
|
|
18
|
+
- **Does every tag have a release entry?**
|
|
19
|
+
- **Who may merge to `main` and `develop`?** A default branch anyone with push rights can merge
|
|
20
|
+
to, or force-push, is a release nobody reviewed.
|
|
21
|
+
- Is the **licence decided and consistent** — the same identifier in `LICENSE`, the manifest and the
|
|
22
|
+
published artefact?
|
|
23
|
+
|
|
24
|
+
## Probes
|
|
25
|
+
|
|
26
|
+
`GOV.mjs`, run by `verify` before the lenses, whose lines you are handed. Read
|
|
27
|
+
from `origin` — a remote-tracking ref outlives the branch's deletion at merge — it
|
|
28
|
+
reports each head merged into `develop` and still there, an `origin` with no `develop`, each name
|
|
29
|
+
outside the model, the version of the contract's `release.version_file` with no tag, no `LICENSE`,
|
|
30
|
+
that manifest with no `license`, each ref this clone keeps for a branch the forge deleted, and
|
|
31
|
+
`fetch.prune` not set to `true`. With `origin` unreachable it could not tell — never an empty
|
|
32
|
+
list, which would read as *no merged branch left*.
|
|
33
|
+
|
|
34
|
+
The forge's own settings — its protected branches and who may merge or push to each — are read by
|
|
35
|
+
no one: say so in one *Limits* line.
|
|
36
|
+
|
|
37
|
+
## For a change
|
|
38
|
+
|
|
39
|
+
**No** — a change says nothing about the branch list, the tag history or the licence.
|
|
40
|
+
|
|
41
|
+
## Signals learned
|
|
42
|
+
|
|
43
|
+
- **The forge's harvest and the clone's drift are two numbers, and only one of them is this
|
|
44
|
+
lens's.** Measured on codelife, at its campaign A3: `origin` held four heads, of which one was
|
|
45
|
+
merged into `develop` and still present, while this clone carried six remote-tracking refs for
|
|
46
|
+
branches the forge had already deleted at merge. The script takes the first from `git ls-remote`
|
|
47
|
+
and the second from the difference; a probe that reads `refs/remotes` for the first reports the second and
|
|
48
|
+
calls it the first.
|
|
49
|
+
- **Zero tags with a `0.0.0` version is a coherent state**, not a defect: nothing has been released.
|
|
50
|
+
Say so rather than filing a missing-tag finding — the defect would be a bumped version *without*
|
|
51
|
+
a tag.
|
|
52
|
+
- **The manifest that must name the licence is the one the artefact ships with** — the contract's
|
|
53
|
+
`release.version_file` — and a root `package.json` that is private and unpublished has no consumer
|
|
54
|
+
to decide it for: its missing `license` key is not a finding. On codelife, a finding was filed
|
|
55
|
+
against exactly that, from a probe that read `package.json` while the shipping manifest agreed
|
|
56
|
+
with `LICENSE`.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Lens GUARD — CI and guards
|
|
2
|
+
|
|
3
|
+
## Objective
|
|
4
|
+
|
|
5
|
+
Say what actually stops a regression from shipping, and whether the guards check facts or merely
|
|
6
|
+
declare intentions. A hook that runs a script that runs nothing is a guard with nothing behind it,
|
|
7
|
+
and it looks green.
|
|
8
|
+
|
|
9
|
+
## Questions
|
|
10
|
+
|
|
11
|
+
Whether the hooks are wired and executable, the tiers exist and run something, and every guard of
|
|
12
|
+
the contract resolves, is the script's to say; a guard pointing at a deleted script never fires and
|
|
13
|
+
never says so.
|
|
14
|
+
|
|
15
|
+
- **What runs, and when?** Map each hook to the script it calls and each script to what it
|
|
16
|
+
actually executes, reading them. Follow it to the end: a tier that runs something may still run
|
|
17
|
+
nothing that matters.
|
|
18
|
+
- Are the hooks **versioned**? `.githooks/` in the repository, `core.hooksPath` set by
|
|
19
|
+
`postinstall` — or a hook that exists only in one developer's `.git/hooks/`, which protects
|
|
20
|
+
nobody else.
|
|
21
|
+
- Does **CI run what the hooks run**? A check that exists only locally is bypassed by anyone who
|
|
22
|
+
pushes from another machine; a check that exists only in CI lets a red commit into the branch.
|
|
23
|
+
- Can a guard be **bypassed silently**? `--no-verify` is always available — the question is
|
|
24
|
+
whether the repository notices afterwards.
|
|
25
|
+
|
|
26
|
+
## Probes
|
|
27
|
+
|
|
28
|
+
`GUARD.mjs`, run by `verify` before the lenses, whose lines you are handed — the
|
|
29
|
+
tiers themselves are the campaign's baseline, already run. It reports `core.hooksPath` unset or
|
|
30
|
+
naming nothing, each hook that is not executable, a `verify:commit` or `verify:push` absent or
|
|
31
|
+
hollow — nothing but `echo` — and each `guards[].run` command not found.
|
|
32
|
+
|
|
33
|
+
## For a change
|
|
34
|
+
|
|
35
|
+
A change weakens no guard — no hook shortened, no check removed, no threshold raised — and a hook it touches checks a fact, not a declaration. A
|
|
36
|
+
weakened guard costs nothing to the change that weakens it, which stays green, and everything to
|
|
37
|
+
the next one.
|
|
38
|
+
|
|
39
|
+
## Signals learned
|
|
40
|
+
|
|
41
|
+
- **A tier that prints `nothing to run` passes.** Found on codelife: `verify:push` guarded the
|
|
42
|
+
moment code leaves the machine and proved nothing. Always follow a script to what it executes,
|
|
43
|
+
never stop at its exit code.
|
|
44
|
+
- A `pre-commit` that reformats files without re-staging them leaves the commit missing the
|
|
45
|
+
formatting it just applied.
|
|
46
|
+
- Hooks wired by `postinstall` only protect someone who has run an install. A fresh clone that is
|
|
47
|
+
read, edited and committed without installing has no hooks at all.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Lens INTENT — Intent
|
|
2
|
+
|
|
3
|
+
## Objective
|
|
4
|
+
|
|
5
|
+
Say whether the code does what was asked of it — what its issues asked of a change, what `docs/spec/`
|
|
6
|
+
specifies of a tree. A change can be correct, tested and clean and still not be the change anyone
|
|
7
|
+
wanted; nothing else in a campaign reads the code against the request.
|
|
8
|
+
|
|
9
|
+
## Questions
|
|
10
|
+
|
|
11
|
+
- **What is missing?** A requirement the request states, a case it cites, something the author
|
|
12
|
+
announces as done — in the description, a commit, a comment — that the code does not do.
|
|
13
|
+
- **What is extra?** Behaviour the request did not ask for, slipped in beside what it did. Not
|
|
14
|
+
wrong by itself: unasked, so unreviewed against any intention.
|
|
15
|
+
- **What is misunderstood?** The code does something close to what was asked and not it — the
|
|
16
|
+
right case under the wrong condition, the rule applied to the wrong set.
|
|
17
|
+
- **Is each verifiable claim of the author true?** *Fixes X*, *covers every caller*, *no behaviour
|
|
18
|
+
change*: each one checked against the code, never taken as given.
|
|
19
|
+
- **In tree scope, does the tree do what the specification states?** Read a rule and its cases,
|
|
20
|
+
then the code or the skill that should do it, and say whether they agree.
|
|
21
|
+
|
|
22
|
+
A drift **quotes both sides and chooses neither**: which one is wrong is the author's decision —
|
|
23
|
+
the specification is obsolete, and a new note records the change, or the implementation is wrong,
|
|
24
|
+
and it gets a fix. The recommendation says so, and is always **untried**: choosing the side is the
|
|
25
|
+
author's.
|
|
26
|
+
Whether a document's *verifiable statements* —
|
|
27
|
+
a command, a path, a version — are true is DOC's; whether the behaviour it *specifies* is done is
|
|
28
|
+
this lens's.
|
|
29
|
+
|
|
30
|
+
## Probes
|
|
31
|
+
|
|
32
|
+
- **The specification's rules and cases**, the referential of a tree-scope run —
|
|
33
|
+
`^#{3,4} [A-Z]+-[0-9]+ — ` over `docs/spec/`. No `docs/spec/` among the tracked files → nothing is
|
|
34
|
+
specified: say so under `Limits`, never compare against a README instead.
|
|
35
|
+
- **What implements a rule** — where a rule names a skill, a command or a file, that name as a whole
|
|
36
|
+
word, a fixed string, outside `docs/spec/`.
|
|
37
|
+
|
|
38
|
+
The probes list what is specified; the method is reading each rule beside its implementation.
|
|
39
|
+
Name in *Method* which rules were read — a campaign cannot read every one, and the ones it did not
|
|
40
|
+
read are a line of *Limits*.
|
|
41
|
+
|
|
42
|
+
## For a change
|
|
43
|
+
|
|
44
|
+
**The issue first** — what it asks, and the rules and cases it cites. A change meets each
|
|
45
|
+
requirement and each cited case, at a place the plan names; adds nothing the issue does not ask;
|
|
46
|
+
and makes no claim, in its commits or its description, that is not true. A requirement no file of
|
|
47
|
+
the plan can meet is said at the plan, before any code.
|
|
48
|
+
|
|
49
|
+
## Signals learned
|
|
50
|
+
|
|
51
|
+
- **This lens ships without campaign history**, written when the specification moved the merge
|
|
52
|
+
request's intention out of DOC's duties. Its first campaigns will write this section.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Lens LEAN — Code that should not exist
|
|
2
|
+
|
|
3
|
+
## Objective
|
|
4
|
+
|
|
5
|
+
Say what code the repository carries that it does not need — dead, duplicating what the platform
|
|
6
|
+
or a dependency already does, abstracted for a second case that never came, or longer than the
|
|
7
|
+
logic it says. Every line is a line the next reader reads and the next review searches; the
|
|
8
|
+
cheapest code to keep true is the code that is gone.
|
|
9
|
+
|
|
10
|
+
## Questions
|
|
11
|
+
|
|
12
|
+
- **What is dead?** A function nothing calls, a branch no input reaches, a flag never set, a file
|
|
13
|
+
nothing references. Not style: dead code is a statement about intent that no longer holds.
|
|
14
|
+
- **What does the standard library, the platform or an installed dependency already do?** A
|
|
15
|
+
hand-written helper beside the built-in that does the same, a second library for one function of
|
|
16
|
+
the first.
|
|
17
|
+
- **Which abstraction has one implementation?** An interface with one class, a factory with one
|
|
18
|
+
product, a parameter every caller passes the same value — an indirection a reader pays for and
|
|
19
|
+
nothing uses.
|
|
20
|
+
- **What shorter form says the same logic?** A loop that is a `map`, a chain of conditions that is
|
|
21
|
+
a table, two near-identical functions that are one with a parameter.
|
|
22
|
+
|
|
23
|
+
A point here is a finding only when its fix removes code: the fix's balance decides, as for every
|
|
24
|
+
lens, and a shorter form that adds a dependency or a layer is not lean.
|
|
25
|
+
|
|
26
|
+
## Probes
|
|
27
|
+
|
|
28
|
+
- **Files nothing references** — a first pass, by name: for each tracked `*.sh`, `*.mjs`, `*.js` or
|
|
29
|
+
`*.ts`, its basename as a whole word, a fixed string, in every other file. Read each file with no
|
|
30
|
+
hit before reporting it: a file can be loaded by a pattern, a manifest or a tool's convention
|
|
31
|
+
rather than by its name.
|
|
32
|
+
- **Exported names used nowhere but their own file** — `export (function|const|class) [A-Za-z0-9_]+`,
|
|
33
|
+
then each name as a whole word, the files that carry it listed. The export syntax is the
|
|
34
|
+
language's: name the one you used in *Method*.
|
|
35
|
+
- **The names that mean "nowhere else to put this"** — tracked paths through a `utils`, `helpers`,
|
|
36
|
+
`misc` or `common` directory; their contents are often a platform's function written again.
|
|
37
|
+
|
|
38
|
+
## For a change
|
|
39
|
+
|
|
40
|
+
A change adds no code that should not exist — a helper the platform already has, an abstraction
|
|
41
|
+
with one implementation, a branch nothing can reach, a longer form of logic the file already says
|
|
42
|
+
shortly — and leaves nothing dead of what it replaced: no old function still there, no flag no
|
|
43
|
+
longer read.
|
|
44
|
+
|
|
45
|
+
## Signals learned
|
|
46
|
+
|
|
47
|
+
- **This lens ships without campaign history**, written when the specification made a
|
|
48
|
+
simplification a finding whichever lens finds it. Its first campaigns will write
|
|
49
|
+
this section.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Lens LINES — Release lines
|
|
2
|
+
|
|
3
|
+
## Objective
|
|
4
|
+
|
|
5
|
+
Say which release lines exist, how far each has drifted from the one that ships — **in both
|
|
6
|
+
directions** — and what carries a fix across them. A line nobody ports to is a line whose users are
|
|
7
|
+
on their own; a line whose divergence nobody has measured is worse, because the decision to keep it
|
|
8
|
+
was never re-taken.
|
|
9
|
+
|
|
10
|
+
## Questions
|
|
11
|
+
|
|
12
|
+
Which older lines exist on the forge and how far each has diverged from `main`, in both directions,
|
|
13
|
+
is the script's to say. What the counts are made of — fixes or features — it does not say, and a
|
|
14
|
+
line of *Limits* says so rather than a guess.
|
|
15
|
+
|
|
16
|
+
- For each older line, **what does it have that the shipping line does not, and what does the
|
|
17
|
+
shipping line have that it does not?** Both directions, always: the second is the backlog of
|
|
18
|
+
unported fixes, the first is what deleting the line would lose.
|
|
19
|
+
- **What ports a fix across** — a written procedure, a habit visible in the history, or nothing? A
|
|
20
|
+
cherry-pick nobody documented is a procedure one person knows.
|
|
21
|
+
- **Is each line's head tagged**, and does its tag agree with the version its manifest declares?
|
|
22
|
+
- **Which lines are supported**, and is that written where a consumer reads it rather than where a
|
|
23
|
+
maintainer remembers it?
|
|
24
|
+
- A line that has diverged in **neither** direction can be closed; a line that has diverged in
|
|
25
|
+
**one** has been abandoned in the other, and which one it is decides who is hurt.
|
|
26
|
+
|
|
27
|
+
## Probes
|
|
28
|
+
|
|
29
|
+
`LINES.mjs`, run by `verify` before the lenses, whose lines you are handed. Read
|
|
30
|
+
from `origin`, it measures each `support/*` line against `main` in both directions and reports a
|
|
31
|
+
line diverged one way only, with its two counts, and a line diverged in neither, which can be
|
|
32
|
+
closed. With `origin` unreachable, or one release line, it could not tell.
|
|
33
|
+
|
|
34
|
+
## For a change
|
|
35
|
+
|
|
36
|
+
**No** — a change lands on one line and says nothing about the distance between lines.
|
|
37
|
+
|
|
38
|
+
## Signals learned
|
|
39
|
+
|
|
40
|
+
- **This lens ships without campaign history**, its subject being a repository with more than one
|
|
41
|
+
release line and the plugin's own having one. Grow it from the first campaign that runs it.
|
|
42
|
+
- **Divergence in one direction only is the finding.** Equal counts both ways are two lines being
|
|
43
|
+
maintained; commits only on the shipping line are an abandoned older line whose users still
|
|
44
|
+
install it. Report the direction, never a single distance.
|
|
45
|
+
- **The porting procedure is the real subject.** Where none is written, the finding is the absence
|
|
46
|
+
and not the arithmetic — the numbers will be different next campaign and the gap will not.
|
|
47
|
+
- A tag that disagrees with its line's manifest is a release nobody can reproduce; it belongs here
|
|
48
|
+
when it is about a line, and to GOV when it is about the repository as a whole.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Lens PERF — Performance & memory
|
|
2
|
+
|
|
3
|
+
## Objective
|
|
4
|
+
|
|
5
|
+
Say what is measured, and whether a regression would be seen. Where nothing is measured, say the
|
|
6
|
+
absence: a number invented to fill this lens becomes a baseline the next campaign compares against,
|
|
7
|
+
and a delta measured from a figure nobody produced is worse than no figure at all.
|
|
8
|
+
|
|
9
|
+
## Questions
|
|
10
|
+
|
|
11
|
+
- **What has a recorded duration or size today, and where is it recorded?** A figure in a commit
|
|
12
|
+
message is not a baseline; a file another campaign reads is one.
|
|
13
|
+
- **Would a regression be seen?** By what — a threshold something enforces, or a person who happens
|
|
14
|
+
to notice the wait? The second is not detection.
|
|
15
|
+
- **What does a contributor wait for**, and what does a consumer wait for? They are rarely the same
|
|
16
|
+
thing, and only one of them is usually measured.
|
|
17
|
+
- **What grows without a bound** — a cache with no eviction, a log nothing rotates, a structure that
|
|
18
|
+
accumulates for the life of the process, an input read whole into memory?
|
|
19
|
+
- Is a **budget pinned** anywhere — a size limit, a timeout, a threshold a guard reads — or is every
|
|
20
|
+
number descriptive?
|
|
21
|
+
- Where the repository is **already slow on purpose**, is the reason written? A deliberate cost
|
|
22
|
+
nobody recorded is indistinguishable from a regression.
|
|
23
|
+
|
|
24
|
+
## Probes
|
|
25
|
+
|
|
26
|
+
- **What a contributor waits for** — the timings `verify` took before the lenses, under `PERF` in
|
|
27
|
+
its block: each script's median and runs, with its command. A timing it *could not tell* is a line
|
|
28
|
+
of `Limits`, with its reason.
|
|
29
|
+
- **What the repository carries, by weight** — the largest tracked files and the whole tree's size,
|
|
30
|
+
from the count, the total and the largest the cut reports in `verify`'s block.
|
|
31
|
+
- **Whether any threshold is enforced anywhere, as opposed to observed** —
|
|
32
|
+
`(timeout|budget|max[-_]?(size|bytes|time)|--max|ulimit)` across the tree. A budget is a number
|
|
33
|
+
compared against something; a number only printed is a measurement.
|
|
34
|
+
- **Is there a baseline file at all** — something a later campaign re-reads: the tracked paths
|
|
35
|
+
matching `baseline|benchmark|perf`, or the line saying nothing in the tree is named as one.
|
|
36
|
+
|
|
37
|
+
A probe whose subject comes back empty is *not applicable* under `Limits`, never a zero: a
|
|
38
|
+
repository with no runtime code has nothing for this lens, and saying so is a measurement.
|
|
39
|
+
|
|
40
|
+
## For a change
|
|
41
|
+
|
|
42
|
+
A change adds no work to a path something already waits on, and removes no number a regression
|
|
43
|
+
would be seen against. Timing the change against its target branch is no objective: two runs on
|
|
44
|
+
one machine, minutes apart, differ by more than most of what it would call a regression.
|
|
45
|
+
|
|
46
|
+
## Signals learned
|
|
47
|
+
|
|
48
|
+
- **This lens ships without campaign history.** It was written when the eleven implemented lenses had
|
|
49
|
+
already run three campaigns and it had run none, so the section below carries what the corpus
|
|
50
|
+
argues rather than what a report measured. Grow it from the first campaign that runs PERF.
|
|
51
|
+
- **The absence is the finding.** *Nothing is measured, so no regression can be seen* is a real
|
|
52
|
+
result with a real consequence, and it is written as a finding rather than left to *Limits* when
|
|
53
|
+
something depends on the speed — a hook a contributor runs at every commit, a build a consumer
|
|
54
|
+
waits for. Where nothing depends on it, it is an observation.
|
|
55
|
+
- **A slow command is not a finding on its own.** What makes it one is a dependency: the command
|
|
56
|
+
runs in a hook, or in a path a person is blocked on. A command nobody waits for being slow is a
|
|
57
|
+
fact, not a defect.
|
|
58
|
+
- **Measure the tier, not the suite.** The thing a contributor actually waits for is whatever the
|
|
59
|
+
contract's verification tiers run, and those are named in the contract rather than guessed.
|
|
60
|
+
- Do not report a number without the command beside it. A duration is the most re-measured figure
|
|
61
|
+
an audit produces and the easiest to produce differently.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Lens PKG — Packaging & tarball
|
|
2
|
+
|
|
3
|
+
## Objective
|
|
4
|
+
|
|
5
|
+
Say whether what a consumer installs is correct: the entry points the manifest declares resolve to
|
|
6
|
+
files the tarball actually contains, the tarball carries nothing private, and nothing executes on
|
|
7
|
+
the consumer's machine at install time without a reason. This lens is the only one that looks at
|
|
8
|
+
the artefact rather than at the repository, and they differ more often than anyone expects.
|
|
9
|
+
|
|
10
|
+
## Questions
|
|
11
|
+
|
|
12
|
+
Whether every entry point is in the tree and in the tarball, whether a lifecycle script runs at
|
|
13
|
+
install time, and whether a tracked file carries a secret, is the script's to say; a
|
|
14
|
+
lifecycle script is read here for its reason — *it builds the package* is a reason that says the
|
|
15
|
+
build should have shipped instead.
|
|
16
|
+
|
|
17
|
+
- **What is in the tarball** that should not be? Something private in it is the finding that costs
|
|
18
|
+
the most.
|
|
19
|
+
- **Are the export conditions complete** — a default present, no condition reachable only by
|
|
20
|
+
accident? (The position of `types` is TYPE's.)
|
|
21
|
+
- **Are the dependency classes right?** A runtime dependency in `devDependencies` breaks the
|
|
22
|
+
consumer's install; a peer expressed as a dependency gives them two copies of it.
|
|
23
|
+
- **Does the committed build match its source**, where the build is versioned? BUILD's script
|
|
24
|
+
reports a tracked file a fresh build changes; a tag whose artefact is behind its source ships old
|
|
25
|
+
code from a repository whose history says otherwise.
|
|
26
|
+
|
|
27
|
+
## Probes
|
|
28
|
+
|
|
29
|
+
`PKG.mjs`, run by `verify` before the lenses, whose lines you are handed. Against
|
|
30
|
+
`npm pack --dry-run --ignore-scripts`, it reports each entry point — `main`, `module`, `types`,
|
|
31
|
+
`typings`, `browser`, `bin`, every `exports` leaf — absent from the tree or left out of the
|
|
32
|
+
tarball; each `preinstall`, `install`, `postinstall` or `prepare` script; each tracked file named
|
|
33
|
+
like a secret; and each line of a tracked file — fixtures and tests included, the history not —
|
|
34
|
+
holding a credential's shape: a private-key header, a GitLab, GitHub or npm token. It prints such
|
|
35
|
+
a value's first four characters and nothing more. With no `package.json` it could not tell.
|
|
36
|
+
|
|
37
|
+
## For a change
|
|
38
|
+
|
|
39
|
+
A change alters what a consumer receives only where the plan says so — an added or removed
|
|
40
|
+
`exports` condition, a moved entry point, a dependency that changed class, a lifecycle script that
|
|
41
|
+
appeared. No path it deletes or renames is one the manifest's `files` or entry points still reach:
|
|
42
|
+
a file leaving the tarball is a break no test catches, because every test runs against the
|
|
43
|
+
repository and not against the artefact, so the pack dry run checks it.
|
|
44
|
+
|
|
45
|
+
A change that touches neither the manifest nor a packaged path has no objective here.
|
|
46
|
+
|
|
47
|
+
## Signals learned
|
|
48
|
+
|
|
49
|
+
- **This lens ships without campaign history**, the plugin's own repository being `kind: none` and
|
|
50
|
+
publishing no package. Grow it from the first library campaign.
|
|
51
|
+
- **Resolution in the repository is not resolution in the tarball.** The two checks are different
|
|
52
|
+
and the second is the one that matters; a probe that only stats the path answers the wrong
|
|
53
|
+
question.
|
|
54
|
+
- **A `prepare` script that builds is a finding, not a convenience.** It moves the build onto the
|
|
55
|
+
consumer's machine, where the toolchain may be absent and the result is unverified — the reason
|
|
56
|
+
a versioned build exists at all.
|
|
57
|
+
- Do not report a missing `exports` map as a defect on its own. Report what a consumer cannot reach
|
|
58
|
+
because of it, or say it is deliberate.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Lens SEC — Security
|
|
2
|
+
|
|
3
|
+
## Objective
|
|
4
|
+
|
|
5
|
+
Say what comes from outside, what it reaches, and what defends it. A security defect is a path
|
|
6
|
+
from an input someone else controls to something they should not touch, with nothing in between;
|
|
7
|
+
the lens traces that path rather than listing vulnerability names.
|
|
8
|
+
|
|
9
|
+
## Questions
|
|
10
|
+
|
|
11
|
+
- **What are the entries?** A request, an argument, an environment variable, a file the code
|
|
12
|
+
imports or parses, a message, a URL a user supplies — anything whose content the repository does
|
|
13
|
+
not write.
|
|
14
|
+
- **What does each entry reach?** A shell, a query, a path on disk, an HTML sink, a deserialiser,
|
|
15
|
+
a redirect, an `eval` — followed through the code, not guessed from a name.
|
|
16
|
+
- **What defends it on the way?** Validation, escaping, an allow-list, a parameterised call, a
|
|
17
|
+
permission check — and does the defence sit on every path, or only the one its author tested?
|
|
18
|
+
- **Where do secrets go?** Read from where, logged where, sent where, kept in memory how long. A
|
|
19
|
+
secret printed into a log or an error message is a leak whatever file it is in.
|
|
20
|
+
- **What does the repository trust that it should not?** A file it downloads and runs, a lockfile
|
|
21
|
+
it does not check, a hook a contributor's machine executes.
|
|
22
|
+
|
|
23
|
+
A secret's *shape* in a tracked file is a fact PKG's script already reports, and an advisory on a
|
|
24
|
+
dependency is DEPS's: this lens asks what the code does with what comes from outside. A finding
|
|
25
|
+
here carries `Affects: consumers` when what ships is exposed, and `critical` when it is exploitable.
|
|
26
|
+
|
|
27
|
+
## Probes
|
|
28
|
+
|
|
29
|
+
- **Sinks worth tracing back to an entry**, in the languages the tree carries —
|
|
30
|
+
`\beval\(|new Function\(|child_process|exec(Sync)?\(|spawn\(|innerHTML|dangerouslySetInnerHTML|\bsh -c\b`.
|
|
31
|
+
- **Shell scripts that run what they download**, or expand an input unquoted into a command —
|
|
32
|
+
`curl[^|]*\| *(ba)?sh|wget[^|]*\| *(ba)?sh`.
|
|
33
|
+
- **Secrets read from the environment, and the lines that print or send them** —
|
|
34
|
+
`process\.env\.[A-Z_]*(TOKEN|SECRET|KEY|PASS)|\$\{?[A-Z_]*(TOKEN|SECRET|KEY|PASS)`.
|
|
35
|
+
|
|
36
|
+
The probes list sinks and secrets; the method is the trace from each back to an entry, or the
|
|
37
|
+
statement that none reaches it. Name in *Method* which sinks were traced.
|
|
38
|
+
|
|
39
|
+
## For a change
|
|
40
|
+
|
|
41
|
+
An entry, a sink, or a path between them that a change adds — a new argument reaching a shell, a
|
|
42
|
+
parsed file reaching the disk, a value reaching HTML — has a defence the objective names. The
|
|
43
|
+
change removes and narrows no defence — no validation deleted, no escape bypassed, no check moved
|
|
44
|
+
after the use — even above a sink it leaves untouched, and prints, logs or sends no secret.
|
|
45
|
+
|
|
46
|
+
## Signals learned
|
|
47
|
+
|
|
48
|
+
- **This lens ships without campaign history.** Its first campaigns will write this
|
|
49
|
+
section.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Lens STANDARDS — Standards
|
|
2
|
+
|
|
3
|
+
## Objective
|
|
4
|
+
|
|
5
|
+
Say whether the code breaks a rule an authority document states, and whether new code has one
|
|
6
|
+
possible shape. A rule written down and not followed is worse than no rule: the next contributor
|
|
7
|
+
reads it, trusts it, and meets code that contradicts it.
|
|
8
|
+
|
|
9
|
+
## Questions
|
|
10
|
+
|
|
11
|
+
- **Which rules do the authority documents state about code?** The `CLAUDE.md`, or whatever file an
|
|
12
|
+
agent reads first, the principles, the glossary — *every HTTP call goes through `src/lib/http.ts`*,
|
|
13
|
+
*a skill never assumes a path*, *a term means one thing*. Only those: a working instruction — how
|
|
14
|
+
to name a branch, how to title a commit, which tone to answer in — guides an agent writing code and
|
|
15
|
+
is not a standard the code can break.
|
|
16
|
+
- **Does the code break one?** A candidate cites the rule, its file and line, and the code that
|
|
17
|
+
breaks it — never a rule paraphrased, never a preference no document states.
|
|
18
|
+
- **Take the newest file and the oldest of a kind. Are they the same shape?** Same error handling,
|
|
19
|
+
same naming, same layout, same way of reporting a result.
|
|
20
|
+
- Where two shapes coexist, is the **boundary written** — a date, a directory, a note saying *the
|
|
21
|
+
old form stays until X*? A migration nobody declared is indistinguishable from an inconsistency.
|
|
22
|
+
- Could a contributor **derive the shape from the code**, or would they need a person to tell them?
|
|
23
|
+
Is a shape **enforced by anything** — a template, a test, a lint rule — or only by whoever reviews?
|
|
24
|
+
|
|
25
|
+
Whether the authority document is itself true is DOC's question; whether the code does what the
|
|
26
|
+
specification asks is INTENT's. This lens reads the documents only for the rules they impose on
|
|
27
|
+
code.
|
|
28
|
+
|
|
29
|
+
## Probes
|
|
30
|
+
|
|
31
|
+
- **The authority documents, and the rules they state about code** — which of `CLAUDE.md`,
|
|
32
|
+
`AGENTS.md`, `docs/spec/principles.md` and `docs/spec/glossary.md` the tracked files hold, and in
|
|
33
|
+
the first two `^\s*[-*0-9.]+ .*\b(never|always|must|only|every)\b`.
|
|
34
|
+
- **One kind of artefact, every instance, the same structural markers** — the kind this repository
|
|
35
|
+
ships most, as a glob over the tracked files, and the markers its instances are supposed to
|
|
36
|
+
share, both read from the tree and named in *Method*: the markers counted in each instance. No
|
|
37
|
+
instance → this probe has no subject, said under `Limits`.
|
|
38
|
+
- **Two instances side by side** — the one carrying the fewest markers beside one carrying them all:
|
|
39
|
+
what the first lacks is the drift.
|
|
40
|
+
|
|
41
|
+
## For a change
|
|
42
|
+
|
|
43
|
+
A change breaks no rule an authority document states about code — the objective cites the rule —
|
|
44
|
+
and adds no second shape for something the repository already has a shape for: every file it adds
|
|
45
|
+
reads like its sibling. The commits, the branch and the description are working instructions, not
|
|
46
|
+
code, and no objective of this lens bears on them.
|
|
47
|
+
|
|
48
|
+
## Signals learned
|
|
49
|
+
|
|
50
|
+
- **Uniformity is a finding too, and a good one.** Measured on codelife itself, whose two kinds
|
|
51
|
+
filled the shape probe as `kind='*.test.mjs'` with
|
|
52
|
+
`^import assert from 'node:assert/strict'|from 'node:test'|from './lib.mjs'`,
|
|
53
|
+
and `kind='*/SKILL.md'` with `^## `: every suite carried the same three markers. Report it;
|
|
54
|
+
a lens that only ever speaks when something is wrong teaches a reader nothing about what is
|
|
55
|
+
right.
|
|
56
|
+
- The strongest signal is **age against shape**: two files written a week apart that look different
|
|
57
|
+
say more than twenty files that look alike.
|
|
58
|
+
- A shape enforced by a **test** rather than by review is the difference between a convention and a
|
|
59
|
+
rule. codelife asserts its skills' anatomy in a suite of its own; that is why the answer there
|
|
60
|
+
was short.
|
|
61
|
+
- Do not report naming preferences. This lens is about a rule someone wrote and a contributor's
|
|
62
|
+
ability to *derive* the next file, not about which name you would have chosen.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Lens TYPE — Public surface & types
|
|
2
|
+
|
|
3
|
+
## Objective
|
|
4
|
+
|
|
5
|
+
Say whether the declarations a consumer receives describe the surface the documentation promises,
|
|
6
|
+
and whether they resolve under every module resolution a consumer may have configured. A package
|
|
7
|
+
whose types resolve under one setting and vanish under another is untyped for half its users, and
|
|
8
|
+
nothing in the repository notices.
|
|
9
|
+
|
|
10
|
+
## Questions
|
|
11
|
+
|
|
12
|
+
Whether a types entry exists, whether its condition comes first wherever it is declared, and
|
|
13
|
+
whether a declaration entry imports a package the consumer does not receive, is the script's to say.
|
|
14
|
+
|
|
15
|
+
- **What does the package export, according to the emitted declarations?** Enumerate it — names,
|
|
16
|
+
and for each whether it is a value, a type, or both — reading the declaration files the manifest
|
|
17
|
+
names, never a path literal.
|
|
18
|
+
- **Does that set match the documented surface**, in both directions? A name in the declarations
|
|
19
|
+
and in no document is surface nobody was told about and everybody may now depend on; a name in a
|
|
20
|
+
document and not in the declarations is a promise the package does not keep. The two differences
|
|
21
|
+
are two findings with different severities: undocumented surface is a `low` that grows, and
|
|
22
|
+
documented surface that does not exist is a `high` the first consumer meets.
|
|
23
|
+
- **Is any exported type effectively opaque** — an `any`, an unexported alias that leaks, a
|
|
24
|
+
parameter typed only by inference from a private shape? Read in context, never counted.
|
|
25
|
+
- **Are the declarations emitted or written by hand?** Hand-written ones drift from the
|
|
26
|
+
implementation silently, and the drift is found by a consumer.
|
|
27
|
+
|
|
28
|
+
## Probes
|
|
29
|
+
|
|
30
|
+
`TYPE.mjs`, run by `verify` before the lenses, whose lines you are handed. It reports
|
|
31
|
+
a package with no types entry, each `exports` object whose `types` condition is not first, and each
|
|
32
|
+
package a declaration entry imports that is in neither `dependencies` nor `peerDependencies`. With
|
|
33
|
+
no `package.json` it could not tell.
|
|
34
|
+
|
|
35
|
+
## For a change
|
|
36
|
+
|
|
37
|
+
A change to the public surface changes the documentation with it. The declarations are the
|
|
38
|
+
subject, not the implementation: a refactor that changes no exported name has no objective here
|
|
39
|
+
however large it is, and a one-line signature change has one. An exported name removed or renamed
|
|
40
|
+
in the files the manifest's `types` and `exports` reach carries a `breaking` marker — the break
|
|
41
|
+
this lens exists to catch.
|
|
42
|
+
|
|
43
|
+
## Signals learned
|
|
44
|
+
|
|
45
|
+
- **This lens ships without campaign history**, the plugin's own repository publishing no
|
|
46
|
+
declarations. Grow it from the first library campaign.
|
|
47
|
+
- **The position of the types condition is the defect, not its absence.** A types condition listed
|
|
48
|
+
after a runtime one is skipped by whichever resolver reads conditions in order, so the package is
|
|
49
|
+
typed for some consumers and not others — and every check that runs inside the repository passes.
|
|
50
|
+
- **Compare against the documentation, not against the implementation.** The implementation and the
|
|
51
|
+
declarations agree by construction when the declarations are emitted; what nothing checks is
|
|
52
|
+
whether either matches what a reader was told.
|
|
53
|
+
- **An emitted declaration file with commits of its own is a contradiction worth reporting**: either
|
|
54
|
+
it is not emitted, or someone has been editing generated output.
|
|
55
|
+
- Do not report an internal type as missing from the surface. This lens asks whether the surface is
|
|
56
|
+
described, never whether it is large enough.
|