janissary 0.5.1 → 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/ai/guidelines/architecture-principles.md +75 -0
  2. package/ai/guidelines/code-guidelines.md +17 -0
  3. package/ai/guidelines/conventional-commits.md +192 -0
  4. package/ai/guidelines/developer-documentation.md +124 -0
  5. package/ai/guidelines/documentation.md +80 -0
  6. package/ai/guidelines/human-writing-guidelines.md +42 -0
  7. package/ai/guidelines/imports-and-barrel-files.md +49 -0
  8. package/ai/guidelines/pull-request-automation.md +98 -0
  9. package/ai/guidelines/strategies-for-readable-summaries.md +27 -0
  10. package/ai/guidelines/user-documentation.md +177 -0
  11. package/ai/personas/algorithm.md +14 -0
  12. package/ai/personas/assistant.md +25 -0
  13. package/ai/personas/link-scout.md +16 -0
  14. package/ai/personas/researcher.md +12 -0
  15. package/ai/personas/security.md +12 -0
  16. package/ai/personas/summarizer.md +11 -0
  17. package/ai/tasks/build-a-feature.md +133 -0
  18. package/ai/tasks/fix-a-small-issue.md +138 -0
  19. package/ai/tasks/improve-codebase.md +187 -0
  20. package/ai/tasks/improve-modularity.md +188 -0
  21. package/ai/tasks/improve-namespacing.md +253 -0
  22. package/ai/tasks/improve-plan.md +121 -0
  23. package/ai/tasks/improve-security.md +144 -0
  24. package/ai/tasks/improve-style.md +147 -0
  25. package/ai/tasks/improve-test-coverage.md +178 -0
  26. package/ai/tasks/merge-change-to-master.md +144 -0
  27. package/ai/tasks/open-feature-pull-request.md +161 -0
  28. package/ai/tasks/plan-ready-features.md +154 -0
  29. package/ai/tasks/prepare-workspace.md +35 -0
  30. package/ai/tasks/quick-commit.md +82 -0
  31. package/ai/tasks/reduce-complexity.md +185 -0
  32. package/ai/tasks/remove-deadcode.md +206 -0
  33. package/ai/tasks/remove-duplication.md +202 -0
  34. package/ai/tasks/update-package.md +137 -0
  35. package/harness-models.json +52 -0
  36. package/package.json +6 -2
  37. package/scripts/changed-files.mjs +22 -0
  38. package/scripts/check-diff.mjs +100 -0
  39. package/scripts/coverage-file.mjs +85 -0
  40. package/scripts/docs-screenshots/capture.mjs +82 -0
  41. package/scripts/docs-screenshots/fixtures/docs/api.md +3 -0
  42. package/scripts/docs-screenshots/fixtures/docs/guide.md +3 -0
  43. package/scripts/docs-screenshots/fixtures/page.html +31 -0
  44. package/scripts/docs-screenshots/fixtures/profiles/demo/editor.json +1 -0
  45. package/scripts/docs-screenshots/fixtures/profiles/demo/writer.json +1 -0
  46. package/scripts/docs-screenshots/fixtures/sample.md +26 -0
  47. package/scripts/docs-screenshots/fixtures/sample.png +0 -0
  48. package/scripts/docs-screenshots/fixtures/sample.ts +25 -0
  49. package/scripts/docs-screenshots/fixtures/src/app.ts +2 -0
  50. package/scripts/docs-screenshots/fixtures/src/tides.ts +2 -0
  51. package/scripts/docs-screenshots/janus.mjs +51 -0
  52. package/scripts/docs-screenshots/manifest.mjs +122 -0
  53. package/scripts/docs-screenshots/scratch.mjs +53 -0
  54. package/scripts/docs-screenshots.mjs +86 -0
  55. package/scripts/lint-files.mjs +47 -0
  56. package/scripts/postinstall.mjs +14 -0
  57. package/scripts/pr-check-changes.sh +15 -0
  58. package/scripts/pr-check-gate.sh +29 -0
  59. package/scripts/pr-check-mergeable.sh +29 -0
  60. package/scripts/pr-commit.sh +23 -0
  61. package/scripts/pr-create-branch.sh +20 -0
  62. package/scripts/pr-create-pr.sh +29 -0
  63. package/scripts/pr-merge.sh +22 -0
  64. package/scripts/pr-push-branch.sh +24 -0
  65. package/scripts/pr-rebase.sh +56 -0
  66. package/scripts/pr-resolve-remote.sh +9 -0
  67. package/scripts/pr-wait-checks.sh +41 -0
  68. package/scripts/publish.mjs +61 -0
  69. package/scripts/release.mjs +209 -0
  70. package/scripts/run.mjs +40 -0
  71. package/web/dist/assets/index-DKEtQh6C.css +1 -0
  72. package/web/dist/assets/index-Dz5j8yq-.js +1066 -0
  73. package/web/dist/index.html +13 -0
@@ -0,0 +1,75 @@
1
+ # architecture principles
2
+
3
+ Guidance for developers and AI assistants evolving this codebase. These principles are derived from the current architecture (a Node server in `src/` driving a React SPA in `web/` over one WebSocket) and from established practice for authoritative client-server, actor-style, and type-safe full-stack systems.
4
+
5
+ When a change conflicts with a principle, that is a design discussion — not a thing to silently work around. Prefer the smallest change that keeps every principle true.
6
+
7
+ ---
8
+
9
+ ## 1. The server is the single source of truth; the client is a thin projection
10
+
11
+ The server owns all state and all rendering decisions. It runs `flattenBuffer` and ships `BufferLine[]` per tab, so the client never re-implements transcript logic — it renders snapshots and emits *intents* (`{ method, params }`), never derived state. This is the authoritative client-server model: the server validates every request and broadcasts a definitive snapshot rather than clients broadcasting isolated events.
12
+
13
+ **Rule.** Presentation logic that affects *what* is shown lives on the server. The client may own only ephemeral, view-local concerns (scroll position, which modal is open, cursor). Never fork a piece of state so both sides compute it — if the client needs it, the server sends it.
14
+
15
+ ## 2. Each agent is one session object that owns its state
16
+
17
+ Today per-tab state is scattered across a dozen parallel maps in `Controller` — `shells`, `cwd`, `busy`, `harnessOf`, `acpSessions`, `acpInfo`, `ptys`, `schedules`, `tabDbConns`, `context`, `browsers`, `workspaces` — all keyed by label. Every new per-tab concern adds another map, and every lifecycle path must remember to touch all of them. The actor model is the antidote: encapsulate one agent's state behind one owner, mutated only through well-defined methods.
18
+
19
+ **Rule.** New per-agent state belongs *on the agent's session object*, not in a new `Map<label, …>` on the controller. An agent owns its shell, PTYs, ACP session, browser, schedule, and connections; nothing reaches into those from outside except through its methods.
20
+
21
+ ## 3. The controller dispatches; feature services execute
22
+
23
+ `controller.ts` is ~1130 lines and violates the project's own 200-line guideline by 5×. It is a classic God Object: command routing, shell I/O, PTY management, ACP loops, browser glue, scheduling, messaging, globbing, file serving, and persistence all in one class. Controller methods should be light — routing and orchestration — with the real work in focused, independently testable modules. (Self-contained command logic has moved out into `src/commands/*.ts` per principle 5; the remaining bulk is the per-tab resource machinery principle 2 targets.)
24
+
25
+ **Rule.** The controller maps an intent to a feature and delegates. Business logic lives in feature modules (`shell`, `db`, `browser-tab`, `schedule`, `messaging`, `acp-loop`, openers). When you find yourself adding a method to the controller, ask whether it belongs to a feature instead. Keep files at or under **200 lines**; a file that exceeds it is signalling that it should be split.
26
+
27
+ ## 4. Parse pure, execute effectful — and never blur the seam
28
+
29
+ The codebase's biggest strength: `parseXCommand` functions (`parseDbCommand`, `parseScheduleCommand`, `parseConnectionCommand`, `parseOpen`, the recognizers) are pure — they return structured data or `{ error }` and do no I/O. Side effects live in the execution layer. This makes parsing trivially testable and routing predictable.
30
+
31
+ **Rule.** A parser returns a value; it never spawns a process, touches the filesystem, mutates state, or writes to a transcript. Effects happen only in the execution layer, which consumes parsed data. Protect this seam — it is why the recognizers and command tests stay simple.
32
+
33
+ ## 5. One command, one definition — delete shadow systems
34
+
35
+ Each command has exactly **one** definition. Self-contained commands (`state`, `next`, `quit`, `hist`, `clear`, `msg`, `broadcast`, `schedule`, `db`) own their logic in `src/commands/<name>.ts` through a `run(command, CommandContext)` method; the controller builds the `CommandContext` for the tab and delegates. Commands that need the tab/PTY/session machinery the controller owns (`agent`, `profile`, `close`, `connection`, `acp`, `open`, `browser`) carry only `name`/`match` and execute in the controller's `runApp`. The earlier shadow system — vestigial `handler` functions plus the 40-field `CommandHandlerContext` describing a React-hook world that no longer existed — has been deleted; `CommandContext` is the single, narrow controller-facing surface.
36
+
37
+ **Rule.** Each command has exactly one definition and one execution path. When a migration leaves scaffolding behind, removing it is part of finishing the migration — not a separate "cleanup" someday. No code path that the system never takes. A new self-contained command belongs in its module behind `CommandContext`; only reach into the controller when a command genuinely needs machinery the context does not expose.
38
+
39
+ ## 6. Every acquire has a matching release (symmetric lifecycle)
40
+
41
+ A tab can own a shell, PTYs, an ACP session and its info, a browser, a workspace clone, served `open` files, schedules, db-connection attributions, and message context. `closeTab` and `shutdown` must today manually tear down each one, in two places, from memory — a leak is one forgotten line away. Actors solve this by giving each entity a single `dispose()`; acquisition and release live together.
42
+
43
+ **Rule.** Resource acquisition and teardown are defined together, by the owner. Closing an agent disposes everything it owns through one call, not a hand-maintained checklist. If you add a resource to an agent, you add its teardown in the same change.
44
+
45
+ ## 7. One wire contract, shared — not mirrored
46
+
47
+ `src/protocol.ts` and `web/src/protocol.ts` are kept in sync by hand ("the web bundle mirrors these locally… keep them in sync"). Manually mirrored types are the textbook cause of type drift: change an event shape on one side, forget the other, and the bug surfaces only at runtime. A single shared definition makes the compiler flag every affected site across both apps.
48
+
49
+ **Rule.** Wire types (`ServerEvent`, `ClientMessage`, `TabView`, …) have exactly one definition that both server and client import. Treat manual mirroring as technical debt to retire, and until then, any change to one file must be made to the other in the same commit.
50
+
51
+ ## 8. Synchronize by minimal, ordered change — not whole-world snapshots
52
+
53
+ `emitState` broadcasts the *entire* `view()` — every tab and every buffer line — on essentially every mutation (keystroke-driven shell output, each ACP chunk, each tick). It is simple and correct today at local-only scale, but it does not bound payload size or survive reconnection gracefully ("what did the client miss?" is the hardest WebSocket problem). The maturity path is sequence numbers or event sourcing so a reconnecting client replays only the delta.
54
+
55
+ **Rule.** Prefer sending what changed over re-sending the world. When adding high-frequency or large state, consider per-tab/targeted updates and a sequence/version so the client can detect gaps and resync. Keep the door open to reconnection-aware sync; don't bake in "full snapshot every event" as a load-bearing assumption.
56
+
57
+ ## 9. Local-first security is an enforced, tested invariant
58
+
59
+ The strongest boundary in the system: the server binds to `127.0.0.1`, requires a per-session token on the WS upgrade, enforces a loopback Host/Origin allowlist (the DNS-rebinding guard), and serves local files *only* from the controller's `/open/<id>` allow-list — an arbitrary path is never reachable. This is verified by tests (bad token rejected, spoofed Host → 403).
60
+
61
+ **Rule.** Every new ingress — an HTTP route, an RPC method, a served file, a spawned process — passes through the same token + Origin/Host guard and, for files, an explicit allow-list. Never widen reach (bind beyond loopback, accept an unauthenticated route) without re-deriving the threat model first, and ship a test that pins the boundary.
62
+
63
+ ## 10. Specs and docs are source of truth — and they evolve with the code
64
+
65
+ `product/specs/` is declared authoritative ("when behavior is ambiguous, the spec is the source of truth"), yet architecture docs here have gone stale before: earlier docs (`AI.md`, a migration map) described components like `cli.tsx` and `src/server/*` paths long after they were removed or flattened into `src/`. Documentation that lies about the code is worse than none — it actively misleads both humans and AI agents.
66
+
67
+ **Rule.** A behavior change updates its `product/specs/` file in the same change; a structural change updates the architecture docs (`CLAUDE.md`'s project-structure section, the affected `ai/guidelines/` files) in the same change. Behavior ships with a test (vitest, colocated `*.test.ts(x)`) — the suite is the executable half of the spec. Stale architecture docs are bugs; fix them on sight.
68
+
69
+ ---
70
+
71
+ ## How to use these
72
+
73
+ - **Developers:** before opening a PR, check it against principles 2–7 (the ones most easily eroded). If you added a `Map<label, …>` to the controller, grew a file past 200 lines, or edited one `protocol.ts` but not the other, reconsider.
74
+ - **AI assistants:** treat these as constraints, not suggestions. When asked to "add a feature," default to a feature module + a session-owned resource, wire one command definition, update the spec and a test, and keep the controller change to delegation. Surface — don't silently resolve — any request that forces a principle to break.
75
+ </content>
@@ -0,0 +1,17 @@
1
+ # Code Guidelines
2
+
3
+ ## File Size
4
+
5
+ - JavaScript/TypeScript files should be limited to **200 lines of code**.
6
+ - When a file exceeds this limit, the **only** acceptable way to shrink it is to
7
+ **extract some of its code into another file** (a new, focused module) and import it
8
+ back. Move a cohesive group of related logic out, don't shave lines off in place.
9
+ - Do **not** try to get under the limit by compacting code, removing comments, or
10
+ deleting blank lines/spacing. These hurt readability without improving the design and
11
+ are not a valid response to the line limit.
12
+
13
+ ## Modularization
14
+
15
+ - Code that belongs to a single feature or functional area should be grouped in its own file.
16
+ - A file should have one clear responsibility. Avoid mixing unrelated concerns in the same file.
17
+ - Prefer small, focused modules over large, monolithic files.
@@ -0,0 +1,192 @@
1
+
2
+ # Conventional Commits 1.0.0
3
+
4
+ ## Summary
5
+
6
+ The Conventional Commits specification is a lightweight convention on top of commit messages.
7
+ It provides an easy set of rules for creating an explicit commit history;
8
+ which makes it easier to write automated tools on top of.
9
+ This convention dovetails with [SemVer](http://semver.org),
10
+ by describing the features, fixes, and breaking changes made in commit messages.
11
+
12
+ The commit message should be structured as follows:
13
+
14
+ ---
15
+
16
+ ```
17
+ <type>[optional scope]: <description>
18
+
19
+ [optional body]
20
+
21
+ [optional footer(s)]
22
+ ```
23
+ ---
24
+
25
+ <br />
26
+ The commit contains the following structural elements, to communicate intent to the
27
+ consumers of your library:
28
+
29
+ 1. **fix:** a commit of the _type_ `fix` patches a bug in your codebase (this correlates with [`PATCH`](http://semver.org/#summary) in Semantic Versioning).
30
+ 1. **feat:** a commit of the _type_ `feat` introduces a new feature to the codebase (this correlates with [`MINOR`](http://semver.org/#summary) in Semantic Versioning).
31
+ 1. **BREAKING CHANGE:** a commit that has a footer `BREAKING CHANGE:`, or appends a `!` after the type/scope, introduces a breaking API change (correlating with [`MAJOR`](http://semver.org/#summary) in Semantic Versioning).
32
+ A BREAKING CHANGE can be part of commits of any _type_.
33
+ 1. _types_ other than `fix:` and `feat:` are allowed, for example [@commitlint/config-conventional](https://github.com/conventional-changelog/commitlint/tree/master/%40commitlint/config-conventional) (based on the [Angular convention](https://github.com/angular/angular/blob/22b96b9/CONTRIBUTING.md#-commit-message-guidelines)) recommends `build:`, `chore:`,
34
+ `ci:`, `docs:`, `style:`, `refactor:`, `perf:`, `test:`, and others.
35
+ 1. _footers_ other than `BREAKING CHANGE: <description>` may be provided and follow a convention similar to
36
+ [git trailer format](https://git-scm.com/docs/git-interpret-trailers).
37
+
38
+ Additional types are not mandated by the Conventional Commits specification, and have no implicit effect in Semantic Versioning (unless they include a BREAKING CHANGE).
39
+ <br /><br />
40
+ A scope may be provided to a commit's type, to provide additional contextual information and is contained within parenthesis, e.g., `feat(parser): add ability to parse arrays`.
41
+
42
+ ## Examples
43
+
44
+ ### Commit message with description and breaking change footer
45
+ ```
46
+ feat: allow provided config object to extend other configs
47
+
48
+ BREAKING CHANGE: `extends` key in config file is now used for extending other config files
49
+ ```
50
+
51
+ ### Commit message with `!` to draw attention to breaking change
52
+ ```
53
+ feat!: send an email to the customer when a product is shipped
54
+ ```
55
+
56
+ ### Commit message with scope and `!` to draw attention to breaking change
57
+ ```
58
+ feat(api)!: send an email to the customer when a product is shipped
59
+ ```
60
+
61
+ ### Commit message with both `!` and BREAKING CHANGE footer
62
+ ```
63
+ feat!: drop support for Node 6
64
+
65
+ BREAKING CHANGE: use JavaScript features not available in Node 6.
66
+ ```
67
+
68
+ ### Commit message with no body
69
+ ```
70
+ docs: correct spelling of CHANGELOG
71
+ ```
72
+
73
+ ### Commit message with scope
74
+ ```
75
+ feat(lang): add Polish language
76
+ ```
77
+
78
+ ### Commit message with multi-paragraph body and multiple footers
79
+ ```
80
+ fix: prevent racing of requests
81
+
82
+ Introduce a request id and a reference to latest request. Dismiss
83
+ incoming responses other than from latest request.
84
+
85
+ Remove timeouts which were used to mitigate the racing issue but are
86
+ obsolete now.
87
+
88
+ Reviewed-by: Z
89
+ Refs: #123
90
+ ```
91
+
92
+ ## Specification
93
+
94
+ The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt).
95
+
96
+ 1. Commits MUST be prefixed with a type, which consists of a noun, `feat`, `fix`, etc., followed
97
+ by the OPTIONAL scope, OPTIONAL `!`, and REQUIRED terminal colon and space.
98
+ 1. The type `feat` MUST be used when a commit adds a new feature to your application or library.
99
+ 1. The type `fix` MUST be used when a commit represents a bug fix for your application.
100
+ 1. A scope MAY be provided after a type. A scope MUST consist of a noun describing a
101
+ section of the codebase surrounded by parenthesis, e.g., `fix(parser):`
102
+ 1. A description MUST immediately follow the colon and space after the type/scope prefix.
103
+ The description is a short summary of the code changes, e.g., _fix: array parsing issue when multiple spaces were contained in string_.
104
+ 1. A longer commit body MAY be provided after the short description, providing additional contextual information about the code changes. The body MUST begin one blank line after the description.
105
+ 1. A commit body is free-form and MAY consist of any number of newline separated paragraphs.
106
+ 1. One or more footers MAY be provided one blank line after the body. Each footer MUST consist of
107
+ a word token, followed by either a `:<space>` or `<space>#` separator, followed by a string value (this is inspired by the
108
+ [git trailer convention](https://git-scm.com/docs/git-interpret-trailers)).
109
+ 1. A footer's token MUST use `-` in place of whitespace characters, e.g., `Acked-by` (this helps differentiate
110
+ the footer section from a multi-paragraph body). An exception is made for `BREAKING CHANGE`, which MAY also be used as a token.
111
+ 1. A footer's value MAY contain spaces and newlines, and parsing MUST terminate when the next valid footer
112
+ token/separator pair is observed.
113
+ 1. Breaking changes MUST be indicated in the type/scope prefix of a commit, or as an entry in the
114
+ footer.
115
+ 1. If included as a footer, a breaking change MUST consist of the uppercase text BREAKING CHANGE, followed by a colon, space, and description, e.g.,
116
+ _BREAKING CHANGE: environment variables now take precedence over config files_.
117
+ 1. If included in the type/scope prefix, breaking changes MUST be indicated by a
118
+ `!` immediately before the `:`. If `!` is used, `BREAKING CHANGE:` MAY be omitted from the footer section,
119
+ and the commit description SHALL be used to describe the breaking change.
120
+ 1. Types other than `feat` and `fix` MAY be used in your commit messages, e.g., _docs: update ref docs._
121
+ 1. The units of information that make up Conventional Commits MUST NOT be treated as case-sensitive by implementors, with the exception of BREAKING CHANGE which MUST be uppercase.
122
+ 1. BREAKING-CHANGE MUST be synonymous with BREAKING CHANGE, when used as a token in a footer.
123
+
124
+ ## Why Use Conventional Commits
125
+
126
+ * Automatically generating CHANGELOGs.
127
+ * Automatically determining a semantic version bump (based on the types of commits landed).
128
+ * Communicating the nature of changes to teammates, the public, and other stakeholders.
129
+ * Triggering build and publish processes.
130
+ * Making it easier for people to contribute to your projects, by allowing them to explore
131
+ a more structured commit history.
132
+
133
+ ## FAQ
134
+
135
+ ### How should I deal with commit messages in the initial development phase?
136
+
137
+ We recommend that you proceed as if you've already released the product. Typically *somebody*, even if it's your fellow software developers, is using your software. They'll want to know what's fixed, what breaks etc.
138
+
139
+ ### Are the types in the commit title uppercase or lowercase?
140
+
141
+ Any casing may be used, but it's best to be consistent.
142
+
143
+ ### What do I do if the commit conforms to more than one of the commit types?
144
+
145
+ Go back and make multiple commits whenever possible. Part of the benefit of Conventional Commits is its ability to drive us to make more organized commits and PRs.
146
+
147
+ ### Doesn’t this discourage rapid development and fast iteration?
148
+
149
+ It discourages moving fast in a disorganized way. It helps you be able to move fast long term across multiple projects with varied contributors.
150
+
151
+ ### Might Conventional Commits lead developers to limit the type of commits they make because they'll be thinking in the types provided?
152
+
153
+ Conventional Commits encourages us to make more of certain types of commits such as fixes. Other than that, the flexibility of Conventional Commits allows your team to come up with their own types and change those types over time.
154
+
155
+ ### How does this relate to SemVer?
156
+
157
+ `fix` type commits should be translated to `PATCH` releases. `feat` type commits should be translated to `MINOR` releases. Commits with `BREAKING CHANGE` in the commits, regardless of type, should be translated to `MAJOR` releases.
158
+
159
+ ### How should I version my extensions to the Conventional Commits Specification, e.g. `@jameswomack/conventional-commit-spec`?
160
+
161
+ We recommend using SemVer to release your own extensions to this specification (and
162
+ encourage you to make these extensions!)
163
+
164
+ ### What do I do if I accidentally use the wrong commit type?
165
+
166
+ #### When you used a type that's of the spec but not the correct type, e.g. `fix` instead of `feat`
167
+
168
+ Prior to merging or releasing the mistake, we recommend using `git rebase -i` to edit the commit history. After release, the cleanup will be different according to what tools and processes you use.
169
+
170
+ #### When you used a type *not* of the spec, e.g. `feet` instead of `feat`
171
+
172
+ In a worst case scenario, it's not the end of the world if a commit lands that does not meet the Conventional Commits specification. It simply means that commit will be missed by tools that are based on the spec.
173
+
174
+ ### Do all my contributors need to use the Conventional Commits specification?
175
+
176
+ No! If you use a squash based workflow on Git lead maintainers can clean up the commit messages as they're merged—adding no workload to casual committers.
177
+ A common workflow for this is to have your git system automatically squash commits from a pull request and present a form for the lead maintainer to enter the proper git commit message for the merge.
178
+
179
+ ### How does Conventional Commits handle revert commits?
180
+
181
+ Reverting code can be complicated: are you reverting multiple commits? if you revert a feature, should the next release instead be a patch?
182
+
183
+ Conventional Commits does not make an explicit effort to define revert behavior. Instead we leave it to tooling
184
+ authors to use the flexibility of _types_ and _footers_ to develop their logic for handling reverts.
185
+
186
+ One recommendation is to use the `revert` type, and a footer that references the commit SHAs that are being reverted:
187
+
188
+ ```
189
+ revert: let us never again speak of the noodle incident
190
+
191
+ Refs: 676104e, a215868
192
+ ```
@@ -0,0 +1,124 @@
1
+ # Writing Developer Documentation
2
+
3
+ Guidance for docs aimed at contributors, not end users: `product/specs/`, `product/plans/`, `ai/guidelines/`
4
+ itself, and the developer-facing parts of `README.md`. The audience already knows how to run the
5
+ app and wants to know how it's built and why — the opposite orientation from
6
+ [[user-documentation]], which assumes no codebase knowledge at all. For sentence-level tone, see
7
+ [[human-writing-guidelines]].
8
+
9
+ ## Specs describe behavior precisely, not just architecture
10
+
11
+ Every file in `product/specs/` is a **behavior spec**, not a design essay: it should let a contributor
12
+ predict exactly what the app does for a given input, including edge cases and error messages,
13
+ without reading the implementation. The existing specs in this repo already establish the house
14
+ style — match it rather than inventing a new one:
15
+
16
+ - **Cross-reference with `[[name]]`** where `name` matches another spec's filename (minus `.md`),
17
+ so related behavior links up without duplicating explanation. A `[[link]]` to a spec that doesn't
18
+ exist yet is fine — it marks something worth writing, not an error.
19
+ - **Name the implementation** where it disambiguates behavior: the function, file, or module that
20
+ owns a piece of logic (`ScheduleManager` in `src/schedule-manager.ts`, `flattenBuffer` in
21
+ `src/tab.ts`). This is the opposite rule from user documentation, which must never do this — a
22
+ spec's reader is the person who will go read that file next.
23
+ - **State the exact error and usage messages**, verbatim, not paraphrased — a contributor
24
+ implementing a caller needs the literal string, and a paraphrase invites drift between the spec
25
+ and the code.
26
+ - **Cover edge cases as a first-class section**, not a footnote: what happens on a duplicate name,
27
+ an empty result, a missing target, a malformed invocation. These are exactly the cases a reader
28
+ can't infer from the happy path alone.
29
+ - **Explain the "why" inline, briefly, at the point of the surprising decision** — not as a
30
+ separate essay. A one-sentence rationale next to the rule it justifies (why secrets are denied
31
+ last in the sandbox profile, why `TMPDIR` is overridden) is worth more than a wall of prose
32
+ elsewhere that the reader has to go find.
33
+
34
+ ## README: minimize time-to-first-success
35
+
36
+ The README is the first developer-facing doc a contributor reads, and its job is narrower than
37
+ it looks: get someone from "I found this repo" to "I ran it / made my first change" as fast as
38
+ possible — not to hold the whole architecture.
39
+
40
+ - The first couple of lines should answer what the project is and why someone would want it;
41
+ everything else is secondary to that.
42
+ - Lead with the smallest working example — the actual install/run commands, verbatim — before
43
+ architecture or design discussion. A contributor's first goal is a working local setup, the same
44
+ "time-to-first-call" instinct API docs optimize for.
45
+ - Push deep detail (architecture rationale, full command reference) out to `product/specs/` and link to it,
46
+ rather than growing the README past what a newcomer needs on day one. A README bloated with
47
+ reference material makes the one thing it must do — get someone running — harder to find.
48
+ - Keep it current the same way as any other doc here: a setup-affecting change updates the README
49
+ in the same PR.
50
+
51
+ ## Onboarding: answer "where do I look first?"
52
+
53
+ A new contributor's first real question is where things live and how to find them — not what
54
+ every subsystem does. The onboarding-relevant docs in this repo (`README.md`, `CLAUDE.md`,
55
+ `ai/guidelines/`) should collectively make that answerable within the first sitting:
56
+
57
+ - State explicitly where behavior is documented (`product/specs/`), where forward-looking design lives
58
+ (`product/plans/`), and where conventions live (`ai/guidelines/`) — a newcomer shouldn't have to guess
59
+ which directory is authoritative for which kind of question.
60
+ - Prefer a single, current source of truth over scattered partial explanations; treat contradicting
61
+ or duplicated guidance across files as a bug to fix, not a style choice.
62
+ - Recently onboarded contributors (or a fresh session with no prior context) are the best source
63
+ of gaps — treat a "how do I even find X" moment as a signal to fix the docs, not just answer the
64
+ question once and move on.
65
+
66
+ ## Technical specs and plans
67
+
68
+ `product/plans/` holds forward-looking design docs for work not yet done (or being done), organized by
69
+ status (`draft/`, `ready/`, `complete/`, `deferred/` — see the root `CLAUDE.md`). Unlike `product/specs/`,
70
+ which documents current behavior, a plan documents an intended change:
71
+
72
+ - Keep a plan only as detailed as needed to make the implementation decision — API/interface
73
+ shape and the reasoning behind a choice, not a line-by-line restatement of every ticket.
74
+ - Cover the rejected alternatives briefly when the choice among them isn't obvious — this is what
75
+ saves a future reader from re-litigating a decision that was already made deliberately.
76
+ - Move the plan file between status folders as its state changes, rather than leaving it in
77
+ `draft/` after the work ships — a stale plan sitting in the wrong folder misleads the next
78
+ person who searches there for what's actually in flight.
79
+ - Use natural line breaks, not fixed-column wrapping, per the existing `CLAUDE.md` convention.
80
+
81
+ ## Record decisions, not just outcomes
82
+
83
+ When a non-obvious architectural or design choice is made — and especially when an alternative was
84
+ seriously considered and rejected — capture the *context, options considered, and consequences*,
85
+ not just the final shape. This is the ADR (architecture decision record) pattern: it exists so a
86
+ future contributor doesn't waste time re-deriving a rationale that already exists, or re-opening a
87
+ question that was already closed for a reason. In this repo that reasoning belongs inline in the
88
+ relevant spec or plan (see "Explain the 'why' inline" above) rather than in a separate decision
89
+ log, unless a decision spans many specs and genuinely needs its own page.
90
+
91
+ ## Comments explain why; specs explain what and when
92
+
93
+ A code comment and a spec entry serve different jobs, and neither substitutes for the other:
94
+
95
+ - A **comment** belongs at the one line where the reasoning isn't recoverable from reading the
96
+ code itself — a workaround for a specific bug, a non-obvious constraint, why the "obvious"
97
+ simpler version doesn't work. This repo's own `CLAUDE.md` rule ("default to no comments; only
98
+ when the WHY is non-obvious") is this principle applied directly — don't restate what the code
99
+ already says clearly.
100
+ - A **spec entry** belongs where a contributor needs to know the behavior *without* reading the
101
+ code at all — the full shape of a command, its edge cases, its error messages (see "Specs
102
+ describe behavior precisely" above). A spec is discoverable and structured; a comment is local
103
+ and easy to miss if you're not already reading that function.
104
+ - Write both at the same time as the change, not after — the reasoning behind a decision is
105
+ cheapest to capture the moment it's made and gets fuzzier (or is skipped entirely) the longer
106
+ it's deferred.
107
+ - An inaccurate comment or spec is worse than none: it actively misleads the next reader instead of
108
+ leaving them to check the code. Treat correctness here as non-negotiable, not a nice-to-have.
109
+
110
+ ## Keep developer docs versioned and reviewed like code
111
+
112
+ - `product/specs/`, `product/plans/`, and `ai/guidelines/` all live in the same repo and are reviewed the same way
113
+ code is — a spec change should be part of the PR that changes the behavior it describes, not a
114
+ follow-up.
115
+ - Prefer several small, focused files over one large one — the same file-size and
116
+ single-responsibility reasoning as [[code-guidelines]] applies to docs.
117
+ - A stale spec is actively worse than no spec: it tells a contributor to implement or trust
118
+ behavior that no longer exists. Treat "the spec still matches the code" as part of the definition
119
+ of done for any behavior-changing PR — a reviewer should check this the same way they check that
120
+ tests were updated, not treat it as optional polish.
121
+ - Docs staying next to the code they describe (`product/specs/` alongside `src/`, not in a separate wiki or
122
+ external tool) is what makes this checkable at review time at all — a reviewer can't be expected
123
+ to cross-reference a system outside the PR diff. If a doc genuinely can't live in-repo, at least
124
+ link to it from the PR description so review can catch drift.
@@ -0,0 +1,80 @@
1
+ # Agent Characters in Documentation
2
+
3
+ Decorative pixel-art Janissary characters appear throughout the user documentation (`documentation/user-documentation/`) as floated sprites that body text wraps around. They add visual rhythm and personality but must follow strict rules so they stay decorative rather than distracting.
4
+
5
+ ## Source images
6
+
7
+ Agent character images live in `agent-images/` at the repo root. Each character has a subdirectory (`malik/`, `idris/`, `yusuf/`, `fariz/`, `hakim/`, `tahir/`) containing one or more character states, each with eight directional rotations. The VitePress build config (`documentation/.vitepress/config.mts`) copies the used facings into `documentation/public/agents/` at build time, trimmed and scaled — that directory is gitignored and must not be committed.
8
+
9
+ ## Allowed facings
10
+
11
+ Only three facings are used in documentation:
12
+
13
+ | Facing | Filename suffix | View description |
14
+ |---|---|---|
15
+ | `south` | `-south.png` | Front-facing, toward the viewer |
16
+ | `south-east` | `-south-east.png` | Three-quarter view, turned slightly right |
17
+ | `south-west` | `-south-west.png` | Three-quarter view, turned slightly left |
18
+
19
+ **Never use `east` or `west` facings.** Those are complete side views and look flat and unnatural at the small display size used in docs. The `FACINGS` array in `config.mts` controls which rotations are copied — keep it in sync with this rule.
20
+
21
+ The `north`, `north-east`, and `north-west` facings (showing the character's back) are available in `agent-images/` but not used in documentation.
22
+
23
+ ## The archer
24
+
25
+ The animated `archer-firing.gif` is the landing-page hero image (`index.md` frontmatter). **The archer must not appear in documentation body pages.** Only the five named characters (malik, yusuf, fariz, hakim, tahir) are used as inline sprites. The idris character is not used in documentation.
26
+
27
+ ## Placement rules
28
+
29
+ ### Count per page
30
+
31
+ - **1 character** on short pages (under ~30 lines)
32
+ - **2 characters** on medium pages (~30–50 lines)
33
+ - **3 characters** on long pages (over ~50 lines)
34
+
35
+ Never place more than 3 characters on a single page, regardless of length.
36
+
37
+ ### Neighboring content
38
+
39
+ A character sprite must be placed **only next to normal paragraph text or headings** (H1–H4). Never place a character adjacent to:
40
+
41
+ - Code blocks (fenced or indented)
42
+ - Screenshots or other images
43
+ - Tables
44
+ - Bullet or numbered lists
45
+ - Blockquotes
46
+
47
+ The sprite floats into the body text, so it needs flowing prose beside it to wrap naturally. A character wedged against a table or code block creates an awkward gap.
48
+
49
+ ### Position in the page
50
+
51
+ Place the character between two block-level elements (heading + paragraph, or paragraph + paragraph, or paragraph + heading). The character tag sits on its own line with a blank line above and below:
52
+
53
+ ```markdown
54
+ ## Section heading
55
+
56
+ <img class="agent-float" src="/agents/tahir-south-west.png" alt="" />
57
+
58
+ Paragraph text that wraps around the sprite...
59
+ ```
60
+
61
+ ### Alternating sides
62
+
63
+ Use the `left` class to float a character to the left instead of the default right. Alternate sides across a page when using multiple characters — one right, one left — so the visual weight balances. The CSS class is `agent-float left` for left-floated, `agent-float` alone for right-floated.
64
+
65
+ ### Vary characters across pages
66
+
67
+ Don't use the same character on consecutive pages in the sidebar order when you can avoid it. Spread the five characters across the documentation so the reader sees variety.
68
+
69
+ ## Adding or changing a character
70
+
71
+ 1. Pick the character and facing from `agent-images/<name>/<state>/rotations/`.
72
+ 2. Reference it as `/agents/<name>-<facing>.png` in the markdown (the build copies it there).
73
+ 3. If you need a facing not in the `FACINGS` array in `config.mts`, add it there — the build picks it up automatically on next run.
74
+ 4. Place the `<img>` tag following the placement rules above.
75
+
76
+ ## Build pipeline
77
+
78
+ The `copyAgentImages()` function in `config.mts` runs at VitePress config load time. For each character directory under `agent-images/`, it finds the first subdirectory (the character state), then copies each facing listed in `FACINGS` through `trimAndScaleSprite()` (which crops transparent padding and upscales 2x) before writing the result to `public/agents/<name>-<facing>.png`. The animated `archer-firing.gif` is copied as-is without trimming.
79
+
80
+ Run `npm run docs:dev` to preview changes locally, or `npm run docs:build` to verify the production build.
@@ -0,0 +1,42 @@
1
+ GUIDELINES FOR NATURAL, HUMAN-SOUNDING WRITING
2
+
3
+ VOICE & TONE
4
+ - Write as if addressing one specific person, not a general audience
5
+ - Use a consistent, grounded voice — avoid a formal "presenter" tone
6
+ - Let personality come through in word choice, not just content
7
+ - Match formality to context; do not default to corporate-neutral language
8
+
9
+ SENTENCE STRUCTURE
10
+ - Mix short and long sentences to create natural rhythm
11
+ - It is acceptable to start sentences with: And, But, So, Because
12
+ - Break grammar rules intentionally when it improves clarity or tone
13
+ - Prefer active voice; use passive voice only when intentional
14
+ - When introducing a topic that will be broken into multiple subtopics or aspects, always lead with a forecast sentence that explicitly names or numbers the upcoming items before elaborating on each one.
15
+
16
+ WORD CHOICE
17
+ - Use concrete, specific words instead of abstract or vague ones
18
+ - Remove filler phrases: "It's worth noting that," "In conclusion," "It's important to remember," "It goes without saying"
19
+ - Avoid overused AI-associated words: utilize, leverage, delve, robust, seamlessly, foster, comprehensive, multifaceted, nuanced, pivotal, transformative, holistic
20
+ - Use contractions naturally: don't, it's, you're, we're, that's
21
+
22
+ STRUCTURE & FLOW
23
+ - Do not open by restating the question or prompt
24
+ - Lead with the main point in the first sentence
25
+ - Use paragraph breaks for pacing, not just topic changes
26
+ - Write in prose when ideas connect naturally; avoid bullet points for flowing content
27
+
28
+ AUTHENTICITY
29
+ - Include mild hedging where appropriate: "probably," "I think," "in most cases," "often"
30
+ - Acknowledge complexity without over-qualifying every statement
31
+ - Take a clear point of view — vague or uncommitted writing feels robotic
32
+ - Use at least one specific example or concrete detail instead of staying abstract
33
+ - It is acceptable to leave some tension unresolved; not everything needs a tidy conclusion
34
+
35
+ WHAT TO AVOID
36
+ - Symmetrical lists that feel generated (e.g., always three examples, three counterpoints, three conclusions)
37
+ - Closing summaries that simply restate what was already said
38
+ - Self-announcing transitions: "Moving on to...," "Now let's discuss...," "Turning to the topic of..."
39
+ - Excessive affirmations or enthusiasm: "Great question!", "Absolutely!", "Certainly!", "Of course!"
40
+ - Overly polished or resolved endings — real writing sometimes ends mid-thought
41
+ - Formulaic structure: introduction → three points → conclusion
42
+ - avoid banned expressions: tailwind, through line
@@ -0,0 +1,49 @@
1
+ # Imports and Barrel Files
2
+
3
+ Prefer **direct imports**. Import each symbol from the module that actually defines it. Do **not** introduce barrel files.
4
+
5
+ A *barrel file* is a module whose only job is to re-export other modules — an `index.ts` full of `export * from './foo.js'` / `export { Bar } from './bar.js'` lines that lets callers write `import { Foo, Bar } from './feature'` instead of reaching into each file. It feels tidy. It is not worth the cost.
6
+
7
+ ## What to do instead
8
+
9
+ ```ts
10
+ // ✅ Direct — import from the file that defines the symbol
11
+ import { AcpLoop } from './acp/loop.js';
12
+ import { HarnessManager } from './harness/manager.js';
13
+
14
+ // ❌ Barrel — import through a re-export hub
15
+ import { AcpLoop, HarnessManager } from './acp/index.js';
16
+ ```
17
+
18
+ Relative imports keep their **`.js`** extension even though the source is `.ts` (NodeNext) — see [`../../CLAUDE.md`](../../CLAUDE.md). A direct import names a real file that "Go to Definition" jumps straight into; a barrel adds a hop that lands you in the re-export list instead.
19
+
20
+ ## The case against barrel files
21
+
22
+ **1. They wreck tooling and build performance.** Importing one symbol through a barrel forces TypeScript, the test runner, and the bundler to load and parse *every* module the barrel re-exports — and everything those modules pull in — even though you use one of them. The whole module subtree becomes an eager dependency of anything that touches the barrel.
23
+
24
+ - Atlassian removed barrel files across ~100,000 files and cut **build minutes by 75%**, made unit tests **~50% faster** locally (up to **10×** in some packages), and dropped CI unit-test counts **88%** (1,600 → 200) because far fewer files were transitively pulled into each test's graph.
25
+ - A Next.js app that loaded **11,000+ modules** (5–10s dev startup) fell to **~3,500 modules** (a 68% reduction) after internal barrels were removed.
26
+
27
+ **2. They defeat tree-shaking and inflate bundles.** Bundlers struggle to prove which re-exports through a barrel are unused, so dead code survives into production. Reported real-world drops after removing barrels: **752 kB → 186 kB** (Angular) and **1.5 MB → 200 kB** (Next.js). A barrel stops being tree-shakeable the moment it contains anything other than pure re-exports — even one `export const x = 5` introduces a possible side effect and disables the optimization.
28
+
29
+ **3. They breed circular dependencies.** When a file imports from the barrel of its *own* directory, it depends on a module that re-exports itself — a cycle. These often work in dev and break in production, or work under one bundler and crash under another, with cryptic errors. Direct imports between siblings (`./loop.js` importing `./manager.js`, never `./index.js`) avoid the loop entirely.
30
+
31
+ **4. They degrade day-to-day DX.** "Go to Definition" lands in the barrel instead of the implementation. Diffs and blame get noisier. The real dependency graph is hidden behind a hub, so it's harder to see what actually depends on what — the opposite of what a namespace directory is supposed to give you.
32
+
33
+ ## The one legitimate exception: a package's public API
34
+
35
+ A barrel is appropriate as the **single published entry point of a library** — the module named by a package's `exports`/`main`, drawing a deliberate boundary between public API and internals. That is a *public surface* decision, made once at a package edge.
36
+
37
+ This codebase is an application, not a published library. We have no such boundary to draw, so we have no reason for barrels. When we group files into a namespace directory (see [`../tasks/improve-namespacing.md`](../tasks/improve-namespacing.md)), callers import the specific file inside it (`./acp/loop.js`), not a `./acp/index.js` hub. **Do not create an `index.ts` barrel where none existed.** The improve-namespacing task only ever converts an *already-existing* bare entry file (e.g. `src/acp.ts`) into `src/acp/index.js`; it never invents a new one.
38
+
39
+ **Rule.** Import every symbol directly from its defining module, with the `.js` extension. Never add a barrel/`index.ts` re-export hub to application code. A namespace is a directory of directly-imported files — not a directory fronted by a re-export index.
40
+
41
+ ---
42
+
43
+ Sources:
44
+
45
+ - [tkdodo.eu — Please Stop Using Barrel Files](https://tkdodo.eu/blog/please-stop-using-barrel-files)
46
+ - [Atlassian Engineering — How We Achieved 75% Faster Builds by Removing Barrel Files](https://www.atlassian.com/blog/atlassian-engineering/faster-builds-when-removing-barrel-files)
47
+ - [DEV — Barrel files and why you should STOP using them now](https://dev.to/tassiofront/barrel-files-and-why-you-should-stop-using-them-now-bc4)
48
+ - [Vercel/Next.js — Tree shaking doesn't work with TypeScript barrel files (#12557)](https://github.com/vercel/next.js/issues/12557)
49
+ - [laniewski.me — Why you should avoid Barrel Files in JavaScript Modules](https://laniewski.me/blog/pitfalls-of-barrel-files-in-javascript-modules/)