-
Notifications
You must be signed in to change notification settings - Fork 0
feat(command): derive COMMAND/ROLE/RESET/HELLO from real server state #471
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
8bc92fd
c588296
31881d9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| # ADD engine transient artifacts — local working state, never committed. | ||
| # (Body matches what `add.py init` scaffolds; this project predates that step.) | ||
| # scope-snapshot.json is the tests->build touch baseline the verify scope-gate | ||
| # reads from disk — the DURABLE scope declaration is the state.json anchor, so | ||
| # the sidecar is regenerable working state. They run 40MB here because the walk | ||
| # covers the whole tree including target/. | ||
| scope-snapshot.json | ||
| pre-archive-state.bak.json | ||
| .update-cache.json |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,205 @@ | ||
| # TASK: Response batch must be encoded in the protocol in effect when each reply was produced | ||
|
|
||
| slug: batch-protocol-version-fidelity · created: 2026-08-12 · stage: production | ||
| autonomy: auto <!-- inherited from the project default (PROJECT.md); explicit level: manual < conservative < auto (visible · overridable) — lower below if a high-risk task needs it, or run `add.py autonomy set`. --> | ||
| phase: ground <!-- ground -> specify -> scenarios -> contract -> tests -> build -> verify -> observe -> done --> | ||
| <!-- high-risk/method-defining scope? declare `risk: high` on the slug line above and lower the | ||
| autonomy level to `manual` or `conservative` — the engine refuses an unguarded completion | ||
| (`unguarded_high_risk_auto`, run.md guard). A comment is never a declaration. --> | ||
|
|
||
| > One file = one task. Fill sections top-to-bottom; the `add` skill drives each phase. | ||
| > When a phase is unclear, read its book chapter in `.add/docs/` (linked per section). | ||
| > The phase marker above is the single source of truth — keep it in sync via `add.py phase`. | ||
|
|
||
| --- | ||
|
|
||
| ## 0 · GROUND — the real codebase ▸ docs/02-the-flow.md | ||
|
|
||
| Touches (files · symbols · signatures): <path:symbol — what it is / how it is keyed> | ||
| Context (working folder): <docs · todos · config · data the task touches — task-delta only> | ||
| Honors (patterns / conventions): <PROJECT.md / CONVENTIONS.md anchors — task-delta only, never a re-scan> | ||
| Anchors the contract cites: <the symbols §3 will name> | ||
|
|
||
| --- | ||
|
|
||
| ## 0 · GROUND — measured, not recalled | ||
|
|
||
| **Found while verifying `client-identity-introspection` (2026-08-12).** Filed rather than folded | ||
| into that PR because the reproducer below touches none of that task's code. | ||
|
|
||
| Moon accumulates the replies for every command in one read batch and serializes them at FLUSH time, | ||
| using whatever protocol version is in effect at the end of the batch. Any command later in the same | ||
| batch that changes the protocol therefore RETRO-ENCODES the earlier replies. Redis writes each | ||
| reply as it is produced, so it does not have this failure mode. | ||
|
|
||
| Measured against this branch's binaries, single-shard, both handlers (monoio default and the tokio | ||
| `handler_single` path) — identical results on both: | ||
|
|
||
| | bytes sent in ONE write | first bytes of the HELLO 3 reply | correct? | | ||
| |--------------------------------|----------------------------------|----------| | ||
| | `HELLO 3\r\n` (alone) | `%7` | yes | | ||
| | `HELLO 3\r\nPING\r\n` | `%7` | yes | | ||
| | `HELLO 3\r\nHELLO 2\r\n` | `*14` | **no** | | ||
| | `HELLO 3\r\nRESET\r\n` | `*14` | **no** | | ||
|
|
||
| `*14` is the RESP2 flattening of the 7-entry map. A client that pipelines `HELLO 3` with any later | ||
| protocol-changing command reads the handshake reply in the wrong protocol and misparses it. | ||
|
|
||
| `HELLO 3` + `HELLO 2` is the ownership proof: it predates and is independent of | ||
| `client-identity-introspection` (no `RESET`, no `ROLE`, no `COMMAND`, no `identity.rs`). RESET, | ||
| which reverts the protocol to RESP2 by contract, simply adds a second trigger for the same | ||
| pre-existing defect. | ||
|
|
||
| Reproducer script: `/tmp/prepipe.sh` (raw `/dev/tcp` writes — `redis-cli` cannot express "two | ||
| commands in one write", which is why this survived). | ||
|
|
||
| Shape of the fix (NOT yet decided — this is §0, not a contract): either flush the accumulated | ||
| responses BEFORE applying a protocol change, or tag each queued response with the protocol version | ||
| current when it was produced. The first is smaller; the second is harder to regress. | ||
|
|
||
|
|
||
| ## 1 · SPECIFY — the rules ▸ docs/03-step-1-specify.md | ||
|
|
||
| Feature: <name> | ||
| Framings weighed: <chosen> (chosen) · <alternative> · <alternative> | ||
| Must: | ||
| <must> | ||
| - <required behavior> | ||
| </must> | ||
| Reject: | ||
| <reject> | ||
| - <bad input / situation> -> "<error_code>" | ||
| </reject> | ||
| After: | ||
| <after> | ||
| - <state that is true once it succeeds> | ||
| </after> | ||
| Assumptions — lowest-confidence first: | ||
| <assumptions> | ||
| ⚠ <the one assumption most likely to be wrong> — lowest confidence because <why>; if wrong: <cost> | ||
| - [ ] <next assumption, ranked> — confirm or deny; never carry an open one forward | ||
| </assumptions> | ||
|
|
||
| <!-- EXIT: every rule stated, every rejection named; assumptions ranked lowest-confidence first, the top one or two ⚠-flagged with why + cost (or, for trivial scope, an honest "none material" that still names the single biggest risk). --> | ||
|
|
||
| --- | ||
|
|
||
| ## 2 · SCENARIOS — pass/fail cases ▸ docs/04-step-2-scenarios.md | ||
|
|
||
| <scenarios> | ||
|
|
||
| ```gherkin | ||
| Scenario: <short name> | ||
| Given <starting situation> | ||
| When <action> | ||
| Then <expected result> | ||
| And <what must remain unchanged> # required for every rejection | ||
| ``` | ||
|
|
||
| </scenarios> | ||
|
|
||
| <!-- EXIT: one scenario per Must AND per Reject; each result is observable. --> | ||
|
|
||
| --- | ||
|
|
||
| ## 3 · CONTRACT — freeze the shape ▸ docs/05-step-3-contract.md | ||
|
|
||
| ``` | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Add language tags to all new contract fences. The same MD040 warning appears in three task records.
🧰 Tools🪛 markdownlint-cli2 (0.23.2)[warning] 107-107: Fenced code blocks should have a language specified (MD040, fenced-code-language) 📍 Affects 3 files
🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||
| <METHOD> <path> body: { <fields> } | ||
| 200 -> { <success fields> } | ||
| 4xx -> { error: "<code>" | "<code>" } | ||
| Schema: <tables/fields touched, and access pattern> | ||
| ``` | ||
|
|
||
| Status: DRAFT | ||
| <!-- The freeze IS the one approval — lead it with the bundle's lowest-confidence flag: the 1–2 | ||
| points most likely wrong across the whole bundle, tagged [spec|scenario|contract|test], each | ||
| with why + cost (the §1 ⚠ assumptions feed it; a flag may point at a scenario or the contract | ||
| too — see run.md). Approved -> Status: FROZEN @ vN — approved by <name>. Changing a frozen | ||
| contract = change request back to SPECIFY. | ||
| EXIT: frozen + every spec rejection has a contracted response + names match GLOSSARY + the | ||
| bundle's lowest-confidence flag was surfaced at the freeze (or an honest "none material"). --> | ||
|
|
||
| --- | ||
|
|
||
| ## 4 · TESTS — failing-first suite (red) ▸ docs/06-step-4-tests.md | ||
|
|
||
| Coverage target: <e.g. 90%> | ||
| Plan (one test per scenario, asserting behavior not internals): | ||
| <test_plan> | ||
| - test_<scenario>: arrange <Given> / act <When> / assert <Then> + assert <unchanged> | ||
| </test_plan> | ||
|
|
||
| Tests live in: `./tests/` · MUST run red (missing implementation) before Build. | ||
| <!-- declare paths as backticked tokens on this line: `./…` = this task dir · | ||
| a token with "/" = project root · a bare name = sibling of the previous | ||
| token's dir · a directory counts its *.py files (non-recursive); reports | ||
| mark declared counts with † · anything resolving outside the project root counts 0 --> | ||
|
|
||
| <!-- EXIT: one test per scenario; suite red for the RIGHT reason; target recorded. --> | ||
|
|
||
| --- | ||
|
|
||
| ## 5 · BUILD — AI writes code ▸ docs/07-step-5-build.md | ||
|
|
||
| Scope (may touch): `./src/` <fill before the §3 freeze — every file the build may write> | ||
| Strategy (ordered batches): <1. … 2. … — the planned build order; guidance, not enforced> | ||
| Safety rule (feature-specific): <e.g. debit+credit in one atomic transaction> | ||
| Code lives in: `./src/` | ||
| Constraints: do NOT change any test or the contract; allow-list packages only; ask if unclear. | ||
|
|
||
| <!-- Scope tokens, backticked, FIRST declaring line: `./…` = this task dir · a token | ||
| with "/" = project root · a bare name = sibling of the previous token's dir · | ||
| outside-root resolutions are dropped fail-closed · a DIRECTORY token covers its | ||
| whole subtree (containment — diverges from §4's non-recursive counting) · | ||
| absent line = UNDECLARED (pre-existing tasks grandfathered, never retro-red) · | ||
| engine enforcement (touched ⊆ declared) lands in scope-gate-enforce. | ||
| EXIT: all green; coverage held; no test/contract touched; no unlisted dependency. --> | ||
|
|
||
| --- | ||
|
|
||
| ## 6 · VERIFY — evidence + non-functional review ▸ docs/08-step-6-verify.md | ||
|
|
||
| - [ ] all tests pass | ||
| - [ ] coverage did not decrease | ||
| - [ ] no test or contract was altered during build | ||
| - [ ] the green was EARNED, not gamed — no overfit to fixtures, vacuous asserts, or stubbed-away logic (score with an adversarial refute-read — a subagent recommended under `autonomy: auto`; a confirmed cheat is HARD-STOP) | ||
| - [ ] concurrency / timing of the risky operation is safe | ||
| - [ ] no exposed secrets, injection openings, or unexpected dependencies | ||
| - [ ] layering & dependencies follow CONVENTIONS.md | ||
| - [ ] a person reviewed and approved the change | ||
|
|
||
| ### Build expectations — what "correct" looks like (fill BEFORE build; confirm each at the gate) | ||
| > Pre-declare the OBSERVABLE outcomes a correct build must produce — derived from §2 SCENARIOS | ||
| > + §3 CONTRACT — so this gate checks the build is RIGHT, not merely that tests are green. Each | ||
| > row is evidence you can SEE, not a restatement of a test name. | ||
| - [ ] <observable outcome a correct build must produce> — confirmed by <how / where> | ||
| - [ ] <another observable outcome> — confirmed by <evidence seen> | ||
|
|
||
| ### Deep checks — do not skim (fill the path that applies; the resolver judges which) | ||
| - [ ] WIRING (code) — every new symbol is referenced; record where / how confirmed | ||
| - [ ] DEAD-CODE (code) — no new unused or orphaned symbol introduced | ||
| - [ ] SEMANTIC (prose / non-code) — read in full, not skimmed: <what read · what confirmed> | ||
|
|
||
| ### GATE RECORD | ||
| Outcome: <PASS | RISK-ACCEPTED | HARD-STOP> | ||
| If RISK-ACCEPTED -> owner: <name> · ticket: <link> · expires: <date> (never for a security gap) | ||
| Reviewed by: <name> · date: <date> | ||
|
|
||
| <!-- A security finding is ALWAYS HARD-STOP. Record exactly one outcome — no silent pass. --> | ||
|
|
||
| --- | ||
|
|
||
| ## 7 · OBSERVE — feed the next loop ▸ docs/09-the-loop.md | ||
|
|
||
| Watch (reuse scenarios as monitors): <error rate / per-rejection rate / latency> | ||
|
|
||
| ### Spec delta | ||
| Forward changes for the next loop — each re-enters at Specify as the next task. One line | ||
| each, tagged `[SPEC · open|seeded|dropped]`, with evidence (e.g. `[SPEC · open] rate-limit | ||
| the retry path (evidence: prod herd spikes)`). See the `add` skill's `deltas.md`. | ||
|
|
||
| ### Competency deltas | ||
| What did this loop teach the foundation? One line each, tagged by competency | ||
| (`DDD · SDD · UDD · TDD · ADD`), status `open`, with evidence. See the `add` skill's `deltas.md`. | ||
| <!-- e.g. - [DDD · open] the model missed multi-tenancy (evidence: scenario_x failed) --> | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
Synchronize the durable scope before recording a verified PASS.
.add/.gitignoreidentifies.add/state.jsonas the durable scope anchor, but the state entries and frozen task records do not describe the same allowed paths. The watch record also admits that its replacement snapshot proves nothing and that a tooling file was out of scope..add/state.json#L220-L231: updateclient-identity-introspection.scope.declaredto the approved task scope..add/state.json#L301-L308: do not leavewatch-cas-transactionsmarked verified until its scope exception is represented..add/state.json#L319-L331: record the actual exception and the approved scope, not only the false-positive whole-tree scan..add/tasks/client-identity-introspection/TASK.md#L453-L458: keep the frozen build scope synchronized with the durable state..add/tasks/watch-cas-transactions/TASK.md#L457-L492: separate.add/tooling/add.pyor record an explicit approved exception; do not use the post-merge self-comparison as scope evidence.📍 Affects 3 files
.add/state.json#L220-L231(this comment).add/state.json#L301-L308.add/state.json#L319-L331.add/tasks/client-identity-introspection/TASK.md#L453-L458.add/tasks/watch-cas-transactions/TASK.md#L457-L492🤖 Prompt for AI Agents