@dialecte/core 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{dialecte-oAXwOG-Q.js → dialecte-CVLWSkgW.js} +1631 -1557
- package/dist/document/query/get/record/get-record.d.ts +14 -1
- package/dist/document/query/get/record/get-record.d.ts.map +1 -1
- package/dist/document/query/get/record/get-records.d.ts +12 -2
- package/dist/document/query/get/record/get-records.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/sqlite-engine.js +14 -10
- package/dist/store/sqlite/runtime/close-engine.d.ts +5 -2
- package/dist/store/sqlite/runtime/close-engine.d.ts.map +1 -1
- package/dist/store/sqlite/runtime/install-pool.d.ts.map +1 -1
- package/dist/store/sqlite/runtime/runtime.types.d.ts +3 -2
- package/dist/store/sqlite/runtime/runtime.types.d.ts.map +1 -1
- package/dist/test/assert-xml/assert-xml.d.ts +4 -2
- package/dist/test/assert-xml/assert-xml.d.ts.map +1 -1
- package/dist/test/assert-xml/index.d.ts +1 -0
- package/dist/test/assert-xml/index.d.ts.map +1 -1
- package/dist/test/assert-xml/xml-assertion-error.d.ts +8 -0
- package/dist/test/assert-xml/xml-assertion-error.d.ts.map +1 -0
- package/dist/test/indexeddb-storage.setup.d.ts +1 -0
- package/dist/test/indexeddb-storage.setup.d.ts.map +1 -0
- package/dist/test.js +154 -148
- package/package.json +40 -4
- package/skills/dialecte/SKILL.md +8 -6
- package/skills/dialecte/references/api.json +5 -5
- package/skills/dialecte/references/api.md +2 -2
- package/skills/dialecte/references/testing.md +167 -11
- package/skills/dialecte-contributing/SKILL.md +1 -1
- package/skills/dialecte-contributing/references/code-rules.md +7 -0
- package/skills/dialecte-contributing/references/landing-changes.md +4 -4
- package/skills/dialecte-lot/SKILL.md +67 -28
- package/skills/dialecte-lot/references/briefs.md +30 -6
- package/skills/dialecte-lot/references/standard-check.md +2 -2
- package/skills/dialecte-lot/references/stations.md +5 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dialecte/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Dialecte - Core functionality for Dialecte SDK",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -52,9 +52,11 @@
|
|
|
52
52
|
"doc:preview": "vitepress preview",
|
|
53
53
|
"//====== TEST ======//": "",
|
|
54
54
|
"test": "npm run test:node && npm run test:browser",
|
|
55
|
-
"test:node": "vitest run --project node --project node-indexeddb --project node-sqlite",
|
|
55
|
+
"test:node": "vitest run --project node --project node-indexeddb --project node-sqlite --project node-indexeddb-documents",
|
|
56
|
+
"test:scope": "tsx scripts/test-scope/run-test-scope.ts",
|
|
56
57
|
"test:browser": "vitest run --project browser",
|
|
57
58
|
"test:browser:all": "vitest run --project browser-all",
|
|
59
|
+
"test:browser:release": "STORE_CONTRACT_FULL_TABLE=1 vitest run --project browser-all",
|
|
58
60
|
"test:perf": "VITE_BENCH=1 vitest run --project perf --mode bench --reporter=verbose",
|
|
59
61
|
"test:perf:node": "VITE_BENCH=1 vitest run --project perf-node --mode bench --reporter=verbose",
|
|
60
62
|
"test:hook": "dialecte gate --hook",
|
|
@@ -86,7 +88,7 @@
|
|
|
86
88
|
"sax": "^1.4.3"
|
|
87
89
|
},
|
|
88
90
|
"devDependencies": {
|
|
89
|
-
"@dialecte/cli": "0.
|
|
91
|
+
"@dialecte/cli": "0.4.0",
|
|
90
92
|
"@tsconfig/node24": "^24.0.4",
|
|
91
93
|
"@types/node": "^24.12.4",
|
|
92
94
|
"@typescript/native": "npm:typescript@^7.0.2",
|
|
@@ -158,8 +160,42 @@
|
|
|
158
160
|
},
|
|
159
161
|
{
|
|
160
162
|
"name": "browser tests",
|
|
161
|
-
"run": "
|
|
163
|
+
"run": "npm run test:scope",
|
|
164
|
+
"counts": "tests",
|
|
165
|
+
"scoped": true
|
|
166
|
+
}
|
|
167
|
+
],
|
|
168
|
+
"release": [
|
|
169
|
+
{
|
|
170
|
+
"name": "check",
|
|
171
|
+
"run": "npm run check"
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
"name": "skills",
|
|
175
|
+
"run": "npm run skills:check"
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
"name": "build",
|
|
179
|
+
"run": "npm run build"
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
"name": "tests",
|
|
183
|
+
"run": "npm run test:node",
|
|
162
184
|
"counts": "tests"
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
"name": "browser tests",
|
|
188
|
+
"run": "npm run test:browser:release",
|
|
189
|
+
"counts": "tests",
|
|
190
|
+
"heavy": true
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
"name": "type narrowing",
|
|
194
|
+
"run": "npm run type-narrowing"
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
"name": "type bench",
|
|
198
|
+
"run": "npm run type-bench:check"
|
|
163
199
|
}
|
|
164
200
|
]
|
|
165
201
|
}
|
package/skills/dialecte/SKILL.md
CHANGED
|
@@ -21,7 +21,7 @@ uuid/name fix-up that a hook already performs on every write. This skill exists
|
|
|
21
21
|
|
|
22
22
|
```
|
|
23
23
|
your app custom extensions, services, UI (yours)
|
|
24
|
-
dialecte package
|
|
24
|
+
a dialecte package @dialecte/<id>, @acme/widget (domain)
|
|
25
25
|
├ definition/*.generated.ts generated from the XSD - never edited by hand
|
|
26
26
|
├ config/ wires the definition into core, `namespace <Name>` types
|
|
27
27
|
├ hooks/ automatic coherence on every write and on import
|
|
@@ -237,8 +237,9 @@ doc.query.myFeature.findOrphanParts({ assembly }) // first param is bound away
|
|
|
237
237
|
(no service, store, composable, component, policy file) and carries none of your product's rules.
|
|
238
238
|
Where your product decides differently from the plain behaviour, see policies below.
|
|
239
239
|
- **Tests on the node tier**: table-driven with the package's runner (`<package>/<version>/test` →
|
|
240
|
-
`run<Name>TestCases`),
|
|
241
|
-
|
|
240
|
+
`run<Name>TestCases`), whole-document XML fixtures, one call of the unit per test (the function
|
|
241
|
+
with `source.query` / `tx`, or its bound method; never both), see references/testing.md. No test
|
|
242
|
+
names a store: the runner runs them in Node on the in-memory
|
|
242
243
|
store. See [Testing](#testing).
|
|
243
244
|
|
|
244
245
|
### Policies: your product's choices, passed per call
|
|
@@ -289,9 +290,10 @@ composable then imports them with `import type`): do not wrap a type only to re-
|
|
|
289
290
|
Each dialecte package exports table-driven runners from its `/test` entry
|
|
290
291
|
(`<package>/<version>/test` → `run<Name>TestCases`; core's own: `@dialecte/core/test`). Details
|
|
291
292
|
and the case format: [references/testing.md](references/testing.md). Short version: one table per
|
|
292
|
-
behaviour,
|
|
293
|
-
|
|
294
|
-
|
|
293
|
+
behaviour, whole-document XML fixtures (inline up to 40 lines, else a sibling `?raw` file) with
|
|
294
|
+
`dev:db-id` ids that say intent, one call of the unit per `act`, `.withExport` asserts the resulting
|
|
295
|
+
XML with hand-written XPath, `.withoutExport` asserts inside `act`, `.generic` for pure functions.
|
|
296
|
+
Refusals assert the error key and nothing staged, never a spy. Red first, one case at a time. No test names its store: the runner chooses it (in Node on the
|
|
295
297
|
in-memory store under a node vitest project whose setup file calls `useTestStorage`, core 0.8+).
|
|
296
298
|
Assertions take XML text; on a big file, check record expectations (tag, attributes, parent,
|
|
297
299
|
count) with the query API instead of XPath on the whole export.
|
|
@@ -681,12 +681,12 @@
|
|
|
681
681
|
"entry": "@dialecte/core/test",
|
|
682
682
|
"otherRoutes": [],
|
|
683
683
|
"signature": "(params: XmlQueriesWithNamespacesParams) => void",
|
|
684
|
-
"summary": "Asserts that each XPath query selects at least one node of the XML text, parsed once for all of them. A failure names the steps that matched, the step that failed, where the last match sits, what the failing step finds there without its predicates, and the XML around it.",
|
|
684
|
+
"summary": "Asserts that each XPath query selects at least one node of the XML text, parsed once for all of them. A failure names the steps that matched, the step that failed, where the last match sits, what the failing step finds there without its predicates, and the XML around it. A query that selects nothing throws an `XmlAssertionError`.",
|
|
685
685
|
"intents": [
|
|
686
686
|
"assert that XML holds the elements XPath queries select",
|
|
687
687
|
"check that an export holds an element"
|
|
688
688
|
],
|
|
689
|
-
"declaredAt": "src/test/assert-xml/assert-xml.ts:
|
|
689
|
+
"declaredAt": "src/test/assert-xml/assert-xml.ts:49"
|
|
690
690
|
},
|
|
691
691
|
{
|
|
692
692
|
"path": "@dialecte/core/test.assertUnexpectedElementQueries",
|
|
@@ -694,12 +694,12 @@
|
|
|
694
694
|
"entry": "@dialecte/core/test",
|
|
695
695
|
"otherRoutes": [],
|
|
696
696
|
"signature": "(params: XmlQueriesWithNamespacesParams) => void",
|
|
697
|
-
"summary": "Asserts that no XPath query selects a node of the XML text, parsed once for all of them.",
|
|
697
|
+
"summary": "Asserts that no XPath query selects a node of the XML text, parsed once for all of them. A query that selects a node throws an `XmlAssertionError`.",
|
|
698
698
|
"intents": [
|
|
699
699
|
"assert that XML does not hold an element",
|
|
700
700
|
"check that an element was removed from an export"
|
|
701
701
|
],
|
|
702
|
-
"declaredAt": "src/test/assert-xml/assert-xml.ts:
|
|
702
|
+
"declaredAt": "src/test/assert-xml/assert-xml.ts:64"
|
|
703
703
|
},
|
|
704
704
|
{
|
|
705
705
|
"path": "@dialecte/core/test.createMockRandomUUID",
|
|
@@ -775,7 +775,7 @@
|
|
|
775
775
|
"make XPath assertions bound to a dialecte's namespaces",
|
|
776
776
|
"assert exported XML in tests"
|
|
777
777
|
],
|
|
778
|
-
"declaredAt": "src/test/assert-xml/assert-xml.ts:
|
|
778
|
+
"declaredAt": "src/test/assert-xml/assert-xml.ts:27"
|
|
779
779
|
},
|
|
780
780
|
{
|
|
781
781
|
"path": "@dialecte/core/test.createXmlSchemaAssertions",
|
|
@@ -481,8 +481,8 @@ ruled out and why in your hand-over.
|
|
|
481
481
|
|
|
482
482
|
| Function | What it answers | Reach for it to |
|
|
483
483
|
| -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
484
|
-
| `assertExpectedElementQueries` | Asserts that each XPath query selects at least one node of the XML text, parsed once for all of them. A failure names the steps that matched, the step that failed, where the last match sits, what the failing step finds there without its predicates, and the XML around it.
|
|
485
|
-
| `assertUnexpectedElementQueries` | Asserts that no XPath query selects a node of the XML text, parsed once for all of them.
|
|
484
|
+
| `assertExpectedElementQueries` | Asserts that each XPath query selects at least one node of the XML text, parsed once for all of them. A failure names the steps that matched, the step that failed, where the last match sits, what the failing step finds there without its predicates, and the XML around it. A query that selects nothing throws an `XmlAssertionError`. | assert that XML holds the elements XPath queries select; check that an export holds an element |
|
|
485
|
+
| `assertUnexpectedElementQueries` | Asserts that no XPath query selects a node of the XML text, parsed once for all of them. A query that selects a node throws an `XmlAssertionError`. | assert that XML does not hold an element; check that an element was removed from an export |
|
|
486
486
|
| `createMockRandomUUID` | The `crypto.randomUUID` a test case's act runs with: UUID-shaped ids that follow from a seed (0 by default), the same sequence on every run. | get deterministic uuids in a test; mock crypto.randomUUID |
|
|
487
487
|
| `createTestHelpers` | Every test helper a dialecte needs, derived from its declaration: the runner, the test project, the record factory, the XPath assertions and the schema assertions. A fixture is checked against the schema of the config it is imported under, then by the dialecte's extra checks, before a case or a test project uses it. | get every test helper of a dialecte; set up a dialecte package's tests; check fixtures against the schema |
|
|
488
488
|
| `createTestProject` | Spin up a Project of the declared dialecte - every config, the default key, its extensions and its own hooks instance; core's own test dialecte when none is given - with the source (and optionally the target) imported under the default config. Returns project + pre-opened documents. Opened inside a running test, the project is destroyed when the test ends (after its other `onTestFinished` hooks); opened outside one (`beforeAll`), the caller destroys it. Spin up a Project from a config, extensions and hooks given one by one, with the source (and optionally the target) imported under the key `default`. | open a project in a test with a fixture imported; get documents to test against |
|
|
@@ -58,24 +58,180 @@ A dialecte package's runner has the same shape with its own types (`<Name>Test.T
|
|
|
58
58
|
|
|
59
59
|
## Rules
|
|
60
60
|
|
|
61
|
+
A test is read far more often than it is written: by the reviewer, by the next person who changes the code,
|
|
62
|
+
by whoever reads a failure. Every rule below keeps one case readable on its own and keeps each behaviour proven
|
|
63
|
+
in exactly one place.
|
|
64
|
+
|
|
65
|
+
### The table
|
|
66
|
+
|
|
61
67
|
- **Red first, one case at a time.** Add one case with `only: true`, run it, see it fail for the
|
|
62
68
|
right reason (an assertion, not a missing module - stub the function with the right type first),
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
then the next case. Remove every `only` before the red is recorded (`dialecte tdd red` refuses a
|
|
70
|
+
focused file), then write the minimum code. Record per case where it failed (`<case title>: failed on
|
|
71
|
+
<assertion>`); a case that never failed is listed with its reason (existing behaviour the change
|
|
72
|
+
touches). A table written after the implementation only pins what the code happens to do.
|
|
73
|
+
- **One table per behaviour.** `.withExport` with `expectedQueries` when the outcome is XML;
|
|
74
|
+
`.withoutExport` only for a returned value or warnings; a refusal's table runs `.withExport` (see
|
|
75
|
+
Refusals below). A case field that picks which assertions run (`check: 'children' | 'references'`,
|
|
76
|
+
`if (testCase.setup === 'x')`) means two behaviours share a table: split it. A flag marking the
|
|
77
|
+
refusal cases of a table is fine.
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
// BAD: one table, the case picks what the act checks
|
|
81
|
+
if (testCase.check === 'children') expect(children).toHaveLength(1)
|
|
82
|
+
else expect(references).toEqual([])
|
|
83
|
+
|
|
84
|
+
// GOOD: one table per behaviour, the same act for every case of a table
|
|
85
|
+
runTestCases.withExport({ testCases: addsChildCases, act: addsChildAct })
|
|
86
|
+
runTestCases.withExport({ testCases: keepsReferencesCases, act: keepsReferencesAct })
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
- **Case keys** read `<initial state> → <outcome>`. No plan, round, station or ticket tags in titles
|
|
90
|
+
or file names (`round 2`, `step 3`, an issue number).
|
|
91
|
+
- **Only the behaviour the change touches.** No preservation cases for code the change leaves alone.
|
|
92
|
+
Each case is written once, for the Node tier; no browser copy of a case. The package's whole-suite
|
|
93
|
+
runs on other tiers or stores are its gate, not duplicates.
|
|
94
|
+
- **Readable size.** Aim for 200-300 lines per test file, one verb or behaviour per file. A file
|
|
95
|
+
growing past that holds two behaviours: split it by behaviour, not by line count.
|
|
96
|
+
|
|
97
|
+
### One call of the unit
|
|
98
|
+
|
|
99
|
+
A verb's test proves that verb's behaviour, nothing else. The `act` makes **one call of the unit under
|
|
100
|
+
test** - the function itself (`fn(tx, params)`, `fn(source.query, params)`) or its bound method
|
|
101
|
+
`tx.<ns>.<verb>`; either is one call, never both in one test - then returns or asserts the outcome.
|
|
102
|
+
|
|
103
|
+
- No second or third call, no retry, no loop over the call.
|
|
104
|
+
- No capture before the call: no snapshot of the staged operations, no copy of the document's metadata,
|
|
105
|
+
to compare after. The outcome is what the call left: `expectedQueries`, a returned value, a staged
|
|
106
|
+
list compared with a literal. "Unchanged" is a case of its own only where it is the behaviour.
|
|
107
|
+
- No XPath on the source inside the `act`, no branch on the case.
|
|
108
|
+
- Not in a verb's test: the transaction's rollback, source-document purity, id or date formats, timestamps,
|
|
109
|
+
provenance details. Their owners test them (core's transaction, the writer of the timestamp, schema
|
|
110
|
+
validation, one acceptance case for source purity); a verb re-proving them breaks when the owner
|
|
111
|
+
changes, not when the verb does.
|
|
112
|
+
- A **reshape** keeps every behaviour covered **by the test that owns it**. List each check it removes
|
|
113
|
+
with the test that covers it, and open that test: it must really assert it, or the reshape adds the case
|
|
114
|
+
there first.
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
// BAD: a state captured before the call, compared after it
|
|
118
|
+
const before = structuredClone(await source.query.getDocumentInfo())
|
|
119
|
+
await source.transaction(async (tx) => addFirstChild(tx, testCase.params))
|
|
120
|
+
expect(await source.query.getDocumentInfo()).not.toEqual(before)
|
|
121
|
+
|
|
122
|
+
// GOOD: one call; the case's expectedQueries carry the outcome
|
|
123
|
+
async function act({
|
|
124
|
+
testCase,
|
|
125
|
+
source,
|
|
126
|
+
}: ActParams<TestDialecteConfig, TestCase>): Promise<ActResult> {
|
|
127
|
+
await source.transaction(async (tx) => addFirstChild(tx, testCase.params))
|
|
128
|
+
return { assertOn: 'source' }
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Refusals assert outcomes, never spies
|
|
133
|
+
|
|
134
|
+
A refusal is proven by what it leaves: the error key, nothing staged, the documents unchanged.
|
|
135
|
+
|
|
136
|
+
- Catch the refusal inside its transaction, assert its key, then `tx.getStagedOperations()` equal to
|
|
137
|
+
`[]`, still inside that transaction.
|
|
138
|
+
- Run the table `.withExport` so the source's `expectedQueries` show the document unchanged.
|
|
139
|
+
- No `vi.spyOn` on a transaction or query method: a spy tests how the code writes, and passes while the
|
|
140
|
+
document is wrong.
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
// BAD: a spy on how it writes
|
|
144
|
+
const addChild = vi.spyOn(tx, 'addChild')
|
|
145
|
+
await expect(addFirstChild(tx, params)).rejects.toThrow()
|
|
146
|
+
expect(addChild).not.toHaveBeenCalled()
|
|
147
|
+
|
|
148
|
+
// GOOD: the key, nothing staged, the document checked by expectedQueries
|
|
149
|
+
await source.transaction(async (tx) => {
|
|
150
|
+
await expect(addFirstChild(tx, testCase.params)).rejects.toMatchObject({
|
|
151
|
+
cause: { key: 'ELEMENT_NOT_FOUND' },
|
|
152
|
+
})
|
|
153
|
+
expect(tx.getStagedOperations()).toEqual([])
|
|
154
|
+
})
|
|
155
|
+
return { assertOn: 'source' }
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Real records, no fakes
|
|
159
|
+
|
|
160
|
+
- Test through the public interfaces: no mocks of internal collaborators, no hand-written fake `Query`,
|
|
161
|
+
no mock of a query module.
|
|
162
|
+
- A step that reads real records gets one real-engine case asserting the values it read, before any
|
|
163
|
+
mock is considered.
|
|
164
|
+
|
|
165
|
+
### Fixtures are whole documents
|
|
166
|
+
|
|
167
|
+
Each case's document is written out in full, so a reader sees the exact input without assembling it in
|
|
168
|
+
their head, and holds only the elements the case needs.
|
|
169
|
+
|
|
170
|
+
- **In the test file** (inline in the case, or one file-level constant used whole) when it is at most
|
|
171
|
+
**40 lines**. Above, a sibling file
|
|
172
|
+
`fixtures/<what-it-holds>.xml` read with a static `?raw` import. A fixture file is at most **120 lines**
|
|
173
|
+
and its name says what it holds (`a-with-two-aa-1.xml`, not `fixture2.xml`).
|
|
174
|
+
- **A variant is its own document**, never a copy edited at runtime: no `.replace()` on a fixture, no
|
|
175
|
+
shared XML constant spliced into a case's document (`${CHILDREN}`, `${HEADER}`), no fixture builder
|
|
176
|
+
functions, no `*.test-fixtures.ts` module. Substitutions inside a tag are fine: names (`${ns}`),
|
|
177
|
+
attribute values (`aA="${UUID}"`), namespace declarations. A shared empty target is fine.
|
|
178
|
+
- A case reading a fixture file carries **one comment** naming what matters in that document
|
|
179
|
+
(`// A "one" holds no AA_1; A "two" holds one without bAA_1`).
|
|
180
|
+
- `dev:db-id` (`CUSTOM_RECORD_ID_ATTRIBUTE`) gives records stable ids; choose ids that say intent
|
|
181
|
+
(`orphaned-ref`, `a-without-child`). Comment the elements that are deliberately absent. No
|
|
182
|
+
`<?xml ?>` declaration in template literals.
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
// BAD: assembled from pieces
|
|
186
|
+
sourceXml: `<Root ${XMLNS_DEFAULT_NAMESPACE}>${A_WITH_CHILDREN.replace('aA="one"', 'aA="two"')}</Root>`,
|
|
187
|
+
|
|
188
|
+
// GOOD: the whole document, inline or as its own file
|
|
189
|
+
import aWithTwoAa1 from './fixtures/a-with-two-aa-1.xml?raw'
|
|
190
|
+
// A "one" holds two AA_1; the second has no bAA_1
|
|
191
|
+
sourceXml: aWithTwoAa1,
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
**Making a variant: a throwaway script, never calls in the test.**
|
|
195
|
+
|
|
196
|
+
1. Start from `dialecte fixture <Tag>` (the smallest valid document holding the tag) or an existing whole
|
|
197
|
+
fixture.
|
|
198
|
+
2. Write the variant with a throwaway script:
|
|
199
|
+
`dialecte run make-<what>.ts --in <base>.xml --out fixtures/<what-it-holds>.xml --seed <n> --now <iso>`
|
|
200
|
+
(seed and clock fixed, so the same command gives the same file). The script may use any tested
|
|
201
|
+
feature, the verb being changed included: its current output is a valid starting document.
|
|
202
|
+
3. Check the result: valid against the schema, and `--expect <file>` (a JSON array of
|
|
203
|
+
`{ tag, where?, under?, count? }`) for what the case needs the document to hold.
|
|
204
|
+
4. Commit only the `.xml`. The script is never committed, and never thrown away before the review
|
|
205
|
+
either: a fixture whose making cannot be rerun cannot be reviewed. Keep it with the change's
|
|
206
|
+
hand-over notes (the folder handed to the reviewer) as `fixture-scripts/make-<what>.ts`, listed in
|
|
207
|
+
`fixture-scripts.md` (`| fixture | script | command |`), and delete it with the notes once the change
|
|
208
|
+
lands. A document written whole by hand needs no script; say so on its line.
|
|
209
|
+
|
|
210
|
+
### Expectations are written by hand
|
|
211
|
+
|
|
212
|
+
- **Expectations are structural XPath with meaningful predicates**, written in the test file from the
|
|
213
|
+
requirement. `//default:AA_1` alone proves nothing; a case can pass because ANOTHER element matched.
|
|
214
|
+
Do not assert on `dev:db-id` in XPath. Pair `unexpectedQueries` with the commented-out source when
|
|
215
|
+
asserting a deletion.
|
|
216
|
+
- **Never generated.** For a document outcome, a document produced by the code under test is never
|
|
217
|
+
the expected result: comparing an export with a saved `.xml` is a snapshot in disguise, it passes on
|
|
218
|
+
whatever the code did the day it was saved.
|
|
219
|
+
|
|
220
|
+
```ts
|
|
221
|
+
// BAD: the code's own output as the truth
|
|
222
|
+
expect(exported).toBe(expectedAfterAddXml)
|
|
223
|
+
|
|
224
|
+
// GOOD: what the requirement says, as queries
|
|
225
|
+
expectedQueries: ['//default:A[@aA="one"]/default:AA_1[@aAA_1="added"]'],
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### The runner and the assertions
|
|
229
|
+
|
|
72
230
|
- **Fixtures are validated against the schema before a case runs** by the runners `createTestHelpers`
|
|
73
231
|
builds - every dialecte package's `run<Name>TestCases` / `create<Name>TestProject` (a dialecte may
|
|
74
232
|
add its own checks, see its skill). Opt out of those extra checks only with a written reason
|
|
75
233
|
(`skipExtraChecks: '<why>'` on `createTestProject`). Core's own `runTestCases` on the dummy
|
|
76
234
|
dialecte (`createTestRunner`) validates nothing.
|
|
77
|
-
- **Test the function directly** (`fn(source.query, params)` / `fn(tx, params)`), not through the
|
|
78
|
-
bound extension method. Use public interfaces; no mocks of internal collaborators.
|
|
79
235
|
- **No test names its store: the runner chooses it.** A node vitest project whose setup file calls
|
|
80
236
|
`useTestStorage({ type: 'inMemory' })` (core 0.8+) runs your tests in Node on the in-memory store;
|
|
81
237
|
without one, the runner's default store. A second project whose setup imports
|
|
@@ -121,7 +121,7 @@ what the station records for the hand-over card).
|
|
|
121
121
|
5. **Independent review**: have a fresh reviewer (a sub-agent with only the spec, the diff and
|
|
122
122
|
these rules, or a human) read the diff against the previous version before hand-over. Self-review
|
|
123
123
|
re-reads your own intent and misses weakened assertions and unusable types.
|
|
124
|
-
6. **Hand over** through `npx dialecte handover <lots.json>`: the agent writes only the lots file, the
|
|
124
|
+
6. **Hand over** through `npx dialecte handover write <lots.json>`: the agent writes only the lots file, the
|
|
125
125
|
command writes the hand-over folder and prints the card the user gets
|
|
126
126
|
([references/landing-changes.md](references/landing-changes.md)). The agent never commits or pushes.
|
|
127
127
|
|
|
@@ -12,6 +12,7 @@ does not name.
|
|
|
12
12
|
- Files and types
|
|
13
13
|
- Naming
|
|
14
14
|
- Comments and committed text
|
|
15
|
+
- Tests
|
|
15
16
|
- Self-check before hand-over
|
|
16
17
|
|
|
17
18
|
## Reuse and sources of truth
|
|
@@ -87,6 +88,12 @@ does not name.
|
|
|
87
88
|
- Do not create documentation that was not asked for, beyond the docs + CHANGELOG the Definition
|
|
88
89
|
of Done requires.
|
|
89
90
|
|
|
91
|
+
## Tests
|
|
92
|
+
|
|
93
|
+
Tests follow the rules of the `dialecte` skill's references/testing.md (section Rules): the one source for
|
|
94
|
+
how a table, a case, a fixture and an expectation are written. Read it before writing a case; this page
|
|
95
|
+
does not repeat it.
|
|
96
|
+
|
|
90
97
|
## Self-check before hand-over
|
|
91
98
|
|
|
92
99
|
```bash
|
|
@@ -8,7 +8,7 @@ script may be waiting or running in the human's terminal.
|
|
|
8
8
|
|
|
9
9
|
## The hand-over
|
|
10
10
|
|
|
11
|
-
The agent writes only a lots file; `npx dialecte handover <lots.json>` does the rest. One lots file per lot
|
|
11
|
+
The agent writes only a lots file; `npx dialecte handover write <lots.json>` does the rest. One lots file per lot
|
|
12
12
|
group, in an uncommitted hand-over folder (for example `_commit-plan/<topic>-<yyyy-mm-dd>/lots.json` at the
|
|
13
13
|
workspace root, outside every repo); the hand-over files are written next to it (`--out <dir>` elsewhere):
|
|
14
14
|
|
|
@@ -64,13 +64,13 @@ workspace root, outside every repo); the hand-over files are written next to it
|
|
|
64
64
|
and is for when the person asks. A heavy hook that does run is rehearsed while another suite loads the
|
|
65
65
|
machine, as it will be when the user commits; when that is not possible, the hand-over notes the machine's
|
|
66
66
|
load at rehearsal time (`uptime`). The real commit's hook always runs.
|
|
67
|
-
- Edited a file after the snapshot (a review fix)? `npx dialecte handover --rehash <lots.json
|
|
67
|
+
- Edited a file after the snapshot (a review fix)? `npx dialecte handover write <lots.json> --rehash` (a cli from 0.2.0 to 0.3: `npx dialecte handover <lots.json> --rehash`).
|
|
68
68
|
- `npx dialecte handover list` shows every hand-over folder with its state (pending, partly committed,
|
|
69
69
|
committed, stale); after a merge, `npx dialecte worktree prune` lists the committed ones with the merged
|
|
70
70
|
worktrees and prints their removal commands: an agent hands them (or the `--yes` command, which asks on
|
|
71
71
|
a terminal) to the user with their `cd`.
|
|
72
|
-
- `handover` comes with `@dialecte/cli` 0.
|
|
73
|
-
`npx
|
|
72
|
+
- `handover write` comes with `@dialecte/cli` 0.4.0 or later; a cli from 0.2.0 to 0.3 takes
|
|
73
|
+
`npx dialecte handover <lots.json>` (the same command 0.4.0 keeps as an alias).
|
|
74
74
|
|
|
75
75
|
## Commit messages
|
|
76
76
|
|
|
@@ -52,7 +52,7 @@ follows the hand-over, and the branch goes up by pull request through that repos
|
|
|
52
52
|
(`dialecte-contributing`). In an app that consumes a dialecte, the lot ends at
|
|
53
53
|
Station 6: what comes after belongs to the app's own process - its PR skill and template for the pull
|
|
54
54
|
request, its own retro and clean-up if it has them - and no further station of this skill runs (no Station 7,
|
|
55
|
-
no `dialecte worktree prune`, no dialecte PR or release step). The hand-over's records (`dialecte recap
|
|
55
|
+
no `dialecte worktree prune`, no dialecte PR or release step). The hand-over's records (`dialecte lot recap <folder>`
|
|
56
56
|
prints a PR's details part from them) feed the app's template only where its rules ask for it. Pushing and
|
|
57
57
|
posting stay the user's (or wait for their approval).
|
|
58
58
|
|
|
@@ -71,9 +71,27 @@ wrong.
|
|
|
71
71
|
|
|
72
72
|
Before the test station, the plan holds what the tests will pin; a gap found by a reviewer costs a round.
|
|
73
73
|
|
|
74
|
+
- **Discovery first**, before any test or code: list what already covers the concept with the whole cli
|
|
75
|
+
surface - `dialecte api find` and the API index, `explain`, `schema`, `fixture`, `standard find` / `show`, the
|
|
76
|
+
package skills - then a code search for what they do not index. The list goes into the lot's `decisions.md`,
|
|
77
|
+
each hit reused or with why not; a shared read found wrong is fixed in place, never forked. Reviewers check the
|
|
78
|
+
diff against that list. The list records evidence: it never limits what a station may use.
|
|
79
|
+
- **An automatic destructive action** (a command or hook that deletes, moves or resets without the person
|
|
80
|
+
running it): the brief opens with its invariant - "never <act> on <thing> unless it is proven <X>, <Y>, <Z>" -
|
|
81
|
+
and a list of adversarial cases written before the tests: for checkouts, other lots, reused or switched
|
|
82
|
+
worktrees and branches, locks, live processes, links; for content, what the target touches, references to
|
|
83
|
+
it, a target emptied and refilled; for both, concurrent runs and an interrupted step. Where proving costs more than
|
|
84
|
+
asking, print the command for the person instead of acting. Keep rules alone let every round find one more
|
|
85
|
+
state.
|
|
86
|
+
- **A step that removes content** (a prune, a redaction, a dedupe): a preservation check per unit (file, page,
|
|
87
|
+
element) proves only the targeted content went, loss counted per unit and never by a summary that hides a
|
|
88
|
+
one-off, and a boundary case (the target touching what must stay) is written before the code.
|
|
89
|
+
- **A rename across many cases** (an output prefix, a message: behaviour, so never an exempt lot): the
|
|
90
|
+
test-station brief states the mapping as a rule (`<call>` → `<expected text>`) and asks for one audit pass
|
|
91
|
+
of every touched file against it before `tdd red`; a text replace without it swaps names in both directions.
|
|
74
92
|
- **The standard** (a lot whose rules come from a standard the code implements):
|
|
75
93
|
[references/standard-check.md](references/standard-check.md) - how it is read, the cross-check for a
|
|
76
|
-
decision, and what the station records for the hand-over card (`dialecte
|
|
94
|
+
decision, and what the station records for the hand-over card (`dialecte lot standard`).
|
|
77
95
|
- **A tooling lot with shared state or an external tool**: the plan carries the store's transition table, its
|
|
78
96
|
invariant and the interleavings, the test-station brief asks one forced-order test per interleaving, and the
|
|
79
97
|
hand-over folder gets a `SMOKE.md` with the lot's one real run; how and where that run happens, and citing
|
|
@@ -131,7 +149,13 @@ test edited after red; the test code changed since its freeze; the cases the cha
|
|
|
131
149
|
--fast` and `--full` check the receipts in their `tdd` step: every test-code file of the change as frozen,
|
|
132
150
|
every new or changed case red then green (or unchanged), the flags and deleted cases listed.
|
|
133
151
|
|
|
134
|
-
**
|
|
152
|
+
**Checks run per step, not at the end of the lot**: the other tests related to the touched files (`vitest
|
|
153
|
+
related` or `--changed <base>`), `check` (its type-check covers the tests) and, in a dialecte package, `dialecte
|
|
154
|
+
rules` on the touched files. The whole suite runs once, at `gate --full`. After any edit of a test (a test-review
|
|
155
|
+
return included, a type-only one too), the test station takes `tdd red` again before `tdd green`: a case greened
|
|
156
|
+
over an unproven edit ends up accepted, not proven.
|
|
157
|
+
|
|
158
|
+
**Exempt lots.** A lot that changes no behaviour - a wording, a rename nothing outputs, a doc-only or a CHANGELOG-only
|
|
135
159
|
lot - has no red to prove. The orchestrator declares it in the brief and writes
|
|
136
160
|
`{ "tdd": "exempt: <reason>" }` to `.dialecte/lot.json` at the package root of the worktree; the gate's
|
|
137
161
|
`tdd` step passes with the reason and what the change touches ("exempt: <reason> (n production files, m
|
|
@@ -152,7 +176,8 @@ Before touching code (the implement station; the test station does the same befo
|
|
|
152
176
|
can assume things that changed. A contradiction stops you: say it, do not reinterpret the plan.
|
|
153
177
|
2. `git status -sb` in your worktree: right branch, clean (or dirty only with your own earlier work).
|
|
154
178
|
Note the HEAD sha for your report.
|
|
155
|
-
3. Load `dialecte` (and `dialecte-contributing` in a dialecte package); run the reuse ladder before writing anything
|
|
179
|
+
3. Load `dialecte` (and `dialecte-contributing` in a dialecte package); run the reuse ladder before writing anything,
|
|
180
|
+
starting from the lot's discovery list in `decisions.md` (Station 0).
|
|
156
181
|
|
|
157
182
|
Then, within the step:
|
|
158
183
|
|
|
@@ -261,8 +286,10 @@ copy of every file `git status --porcelain -z --untracked-files=all` lists at th
|
|
|
261
286
|
untracked, at its path relative to the repository root, and beside it `round-<n>.deleted`, NUL-separated, naming
|
|
262
287
|
each deleted file and the old path of each rename (beside the copy, never in it: it is no file of the lot). A copy,
|
|
263
288
|
not a patch: nothing to apply, binary files included. The lot stays uncommitted until the hand-over, so HEAD is the
|
|
264
|
-
base for the whole loop: these snapshots are the only refs the later rounds have.
|
|
265
|
-
|
|
289
|
+
base for the whole loop: these snapshots are the only refs the later rounds have. Every round gets one, round 1
|
|
290
|
+
and a verify-only round included: without it a throwaway worktree holds the only copy of that round's state,
|
|
291
|
+
lost when the throwaway is removed. From cli 0.4.0,
|
|
292
|
+
`dialecte lot snapshot <folder> --round <n>` takes it (and `dialecte lot delta` reads it); the text here is
|
|
266
293
|
the manual fallback for an older cli.
|
|
267
294
|
|
|
268
295
|
- **Round 1 - full review**, blind to the implementer's conclusions, every severity. The checklist, each angle
|
|
@@ -276,8 +303,8 @@ the manual fallback for an older cli.
|
|
|
276
303
|
the findings ledger. The last round reports what its class acts on; anything below is logged as a follow-up, not
|
|
277
304
|
fixed in the lot. A finding the last round acts on is fixed; the round that would verify the fix goes to the user
|
|
278
305
|
(above). The floor rises so the rounds converge. The review has three parts:
|
|
279
|
-
- (a) **Each fix verified on the old code.** From cli 0.
|
|
280
|
-
`dialecte
|
|
306
|
+
- (a) **Each fix verified on the old code.** From cli 0.4.0,
|
|
307
|
+
`dialecte lot delta <folder> --round <n> --worktree` makes the throwaway worktree holding `round-<n-1>/`
|
|
281
308
|
(the fixer's, when it made one: Station 4, reused, never a second); with an older cli, the orchestrator makes one by hand on its own branch
|
|
282
309
|
(never the lot's), copies the snapshot over it, removes its deleted files - the one place a `git rm` is
|
|
283
310
|
allowed, never in the lot's worktree - and passes the path (`worktree prune` lists it after the lot):
|
|
@@ -313,13 +340,15 @@ the manual fallback for an older cli.
|
|
|
313
340
|
- (c) **One line naming what round 1 did not look at.**
|
|
314
341
|
|
|
315
342
|
**The findings ledger** (`findings-ledger.md` in the hand-over folder) is the lot's memory across rounds. Only the
|
|
316
|
-
orchestrator writes it, by hand or, from cli 0.
|
|
317
|
-
`dialecte
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
343
|
+
orchestrator writes it, by hand or, from cli 0.4.0, with
|
|
344
|
+
`dialecte lot finding <folder> --round <n> --severity <blocking|should-fix|nit> --at <file:line> <finding>`
|
|
345
|
+
(a new row, `open`) and
|
|
346
|
+
`dialecte lot decide <folder> <finding> <open | fix-to-verify | fixed <round> [note] | accepted <reason> | logged <where>>`
|
|
347
|
+
(the ledger row spells the state `fix to verify`). The cli writes neither the risk-class line nor `reopened`,
|
|
348
|
+
and its `fix-to-verify` carries no blast radius: write those by hand, or keep the blast-radius lines in a fixes
|
|
349
|
+
file beside the ledger. Reviewers propose rows. It opens with the risk class (`Risk class: <class> - <rounds>
|
|
350
|
+
rounds, last round acts on <floor>, a further round only with the user - set by <files>`). One row per finding:
|
|
351
|
+
id, round raised, severity, file:line, state - `open`, `fix to verify - <blast radius>` (the fixer says fixed; its
|
|
323
352
|
blast-radius line comes with it), `fixed <n>` (n = the round that verified the fix; `fixed <n> (reasoned)` when the
|
|
324
353
|
guard was not run), `reopened <n> by <id>` (a later fix broke it again), `accepted <reason>`, `logged <follow-up>`.
|
|
325
354
|
Every reviewer reads it; a finding fixed or accepted is not raised again unless the fix is wrong. Outside every
|
|
@@ -362,8 +391,8 @@ that fixes one finding and hides another, a fix that undoes an earlier one.
|
|
|
362
391
|
|
|
363
392
|
**The red of a fix's new test comes from the tree the round reviewed, never from production put back.** Write the
|
|
364
393
|
test before the fix and take its red with `npx dialecte tdd red` while the code it pins is still the reviewed one.
|
|
365
|
-
When production already holds the fix, run the new test where the old code still lives: `npx dialecte
|
|
366
|
-
<folder> --round <n+1> --worktree` (n = the round whose findings you fix; cli 0.
|
|
394
|
+
When production already holds the fix, run the new test where the old code still lives: `npx dialecte lot delta
|
|
395
|
+
<folder> --round <n+1> --worktree` (n = the round whose findings you fix; cli 0.4.0, else the orchestrator's
|
|
367
396
|
hand-made throwaway of Station 3 (a)) makes a throwaway worktree holding `round-<n>/` and prints its removal
|
|
368
397
|
command; copy the new test file in, with the test code it imports that the round changed, run it there - it must
|
|
369
398
|
fail on its assertion - and in the lot's worktree, where it passes. The report says the red was taken on
|
|
@@ -381,12 +410,13 @@ it from a test-first red (it flags `red after green` only when the case already
|
|
|
381
410
|
|
|
382
411
|
## Station 6 - hand over
|
|
383
412
|
|
|
384
|
-
Write only the lots file and run
|
|
385
|
-
<lots.json>`: it writes the hand-over folder and prints its card. Never a
|
|
386
|
-
commit. When the repository has a commit hook (`core.hooksPath`),
|
|
387
|
-
run <folder> --rehearse`: a scratch clone holding your tree, the
|
|
388
|
-
runs as it will for the user. Rehearse a heavy hook while another
|
|
389
|
-
the load at rehearsal time (`uptime`) after the card: a quiet
|
|
413
|
+
Write only the lots file and run
|
|
414
|
+
`npx dialecte handover write <lots.json>`: it writes the hand-over folder and prints its card. Never a
|
|
415
|
+
hand-written script, nor a file per commit. When the repository has a commit hook (`core.hooksPath`),
|
|
416
|
+
rehearse first with `npx dialecte handover run <folder> --rehearse`: a scratch clone holding your tree, the
|
|
417
|
+
hooks folder and `node_modules`, so the hook runs as it will for the user. Rehearse a heavy hook while another
|
|
418
|
+
suite loads the machine, or at least note the load at rehearsal time (`uptime`) after the card: a quiet
|
|
419
|
+
rehearsal hides a time bound that fails under load.
|
|
390
420
|
|
|
391
421
|
**A check's result belongs to the tree it ran on.** From cli 0.2.0 every gate and hook result is recorded with
|
|
392
422
|
what it checked - the tree, the installed lock, the linked `file:` dependencies and the command - and a run with
|
|
@@ -411,7 +441,9 @@ never a pasted or retyped card, which goes stale as soon as a record changes. Wi
|
|
|
411
441
|
**is the card** `handover` printed - repository, worktree, branch, HEAD, gates, the lots with their files and
|
|
412
442
|
messages, and the command with its absolute `cd` - pasted as printed, never retyped. After it: what was not verified, and one usage line (the lot's sub-agent tokens, in total
|
|
413
443
|
and by station: [references/retro.md](references/retro.md)). A refusal is a finding: fix the file or the
|
|
414
|
-
message, do not loosen the lots.
|
|
444
|
+
message, do not loosen the lots. Before reporting, stop every background task you started and check with `ps`
|
|
445
|
+
that none is left: a forgotten wait loop holds the worktree, and `lot close` keeps it, for as long as it runs.
|
|
446
|
+
`npx dialecte handover list` shows every hand-over waiting on the machine.
|
|
415
447
|
Never commit, push, stage, or touch a repository's index while a hand-over may be waiting.
|
|
416
448
|
|
|
417
449
|
## Station 7 - retro
|
|
@@ -433,10 +465,15 @@ that is rewritten every retro never settles.
|
|
|
433
465
|
in its worktree before anything else. Its hygiene step fails on a staged file or a stash added since the
|
|
434
466
|
worktree was made, whatever wrote it. A failure is a finding: look with the read-only commands it prints
|
|
435
467
|
and report it; never repair it with a git command that writes. When the agent changed nothing, the gate
|
|
436
|
-
still runs its hygiene and `tdd` step and answers the rest from its record of that tree.
|
|
468
|
+
still runs its hygiene and `tdd` step and answers the rest from its record of that tree. Then check with `ps`
|
|
469
|
+
that no task the agent started is still running in its worktree: one left is stopped and reported.
|
|
437
470
|
- Write each brief from [references/briefs.md](references/briefs.md): the brief is the agent's whole
|
|
438
471
|
context; synthesise, do not say "based on the findings". Every brief carries its lines for all stations
|
|
439
|
-
(nothing under `.git` by hand, no workflow without the user's opt-in, no `rm` on a variable path
|
|
472
|
+
(nothing under `.git` by hand, no workflow without the user's opt-in, no `rm` on a variable path, files
|
|
473
|
+
edited with the edit tool, a rewriting script reads before it opens for writing, no ad-hoc file-writing
|
|
474
|
+
script with a lot's worktree as its working folder: it runs from the scratchpad or the hand-over folder; no
|
|
475
|
+
real person's name or handle in any file, test values included; every background task it started stopped,
|
|
476
|
+
checked with `ps`, before it reports).
|
|
440
477
|
- Each station agent is defined once, for every host, in [references/stations.md](references/stations.md):
|
|
441
478
|
its tier, its access (`read`, `review`: runs commands but edits nothing, `edit`) and its role (`test-writer`,
|
|
442
479
|
`test-reviewer`, `implementer`, `implementer-strong`, `reviewer`, `fixer`, `eval-runner`, `grader`,
|
|
@@ -477,8 +514,8 @@ orchestrator launches in the background gets a **deadline** when it is launched:
|
|
|
477
514
|
you - a scheduled check or a wake-up at the deadline, or a monitor on the run's log - so you are woken even
|
|
478
515
|
if no notice comes. A host with none polls at the deadline.
|
|
479
516
|
- **At the deadline, look at the run itself**: its process tree (`pgrep -P <pid>`, `ps -o pid,stat,time,command`:
|
|
480
|
-
is its CPU time growing?), the agent's last activity or the log's last line and its time, and
|
|
481
|
-
|
|
517
|
+
is its CPU time growing?), the agent's last activity or the log's last line and its time, and a list of the
|
|
518
|
+
machine's live runs, each with its state, once the cli has one.
|
|
482
519
|
- **Kill on what you see, never on the clock.** No new output and no CPU time gained by the tree since the
|
|
483
520
|
last look: it is stuck - tell the user what you saw (the tree, the last line, since when) and stop or
|
|
484
521
|
restart it. Still gaining CPU time or writing output: set one more deadline. Past that second deadline it
|
|
@@ -521,6 +558,8 @@ hooks. What differs is how a host starts a session, spawns an agent and wakes yo
|
|
|
521
558
|
- `rm`, `mv` or `xargs` on a variable path: leave temporary files in the hand-over folder or the scratchpad.
|
|
522
559
|
- Wait on a background run with no deadline, or on its completion notice alone.
|
|
523
560
|
- Hand over a hand-written commit or merge script.
|
|
561
|
+
- Write a real person's name or handle in any file, test values included: neutral names (`reviewer`,
|
|
562
|
+
`person-a`).
|
|
524
563
|
- Implement two lots in one station "because they are small".
|
|
525
564
|
- Reach for `any`, `@ts-ignore` or a skipped test to get green.
|
|
526
565
|
- Let an output live only in the chat.
|