jinaga-server 3.7.5 → 3.8.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/.claude/skills/degrees-of-freedom/.openskills.json +7 -0
- package/.claude/skills/degrees-of-freedom/SKILL.md +18 -0
- package/.claude/skills/degrees-of-freedom/degrees-of-freedom-constitution.md +142 -0
- package/.claude/skills/night-shift-setup/.openskills.json +7 -0
- package/.claude/skills/night-shift-setup/SKILL.md +95 -0
- package/.claude/skills/night-shift-setup/configuring-a-repository.md +111 -0
- package/.claude/skills/night-shift-setup/creating-the-log.md +59 -0
- package/.claude/skills/night-shift-setup/creating-the-routines.md +80 -0
- package/.claude/skills/night-shift-setup/log-model.factual +971 -0
- package/.claude/skills/night-shift-worker/.openskills.json +7 -0
- package/.claude/skills/night-shift-worker/SKILL.md +199 -0
- package/.claude/skills/night-shift-worker/night-shift-log.md +72 -0
- package/.claude/skills/night-shift-worker/scripts/register-stack.sh +173 -0
- package/.claude/skills/night-shift-worker/stacking.md +57 -0
- package/.claude/skills/night-shift-worker/verifying-a-merged-fix.md +16 -0
- package/.claude/skills/refining-issues/.openskills.json +7 -0
- package/.claude/skills/refining-issues/SKILL.md +327 -0
- package/.claude/skills/refining-issues/references/worked-example.md +234 -0
- package/.github/workflows/main.yml +2 -2
- package/.night-shift/config.md +84 -0
- package/.night-shift/skills.md +17 -0
- package/README.md +21 -0
- package/dist/http/router.js +19 -23
- package/dist/http/router.js.map +1 -1
- package/dist/http/stream.d.ts +0 -1
- package/dist/http/stream.js +6 -17
- package/dist/http/stream.js.map +1 -1
- package/dist/jinaga-server.js +74 -7
- package/dist/jinaga-server.js.map +1 -1
- package/integration-test/jinaga-test/package-lock.json +4 -4
- package/integration-test/jinaga-test/package.json +1 -1
- package/package.json +2 -2
- package/src/http/router.ts +31 -39
- package/src/http/stream.ts +21 -36
- package/src/jinaga-server.ts +92 -4
- package/test/http/streamLogVolumeSpec.ts +284 -0
- package/test/jinaga-server/poolConfigurationSpec.ts +147 -0
- package/test/jinaga-server/tracePoolSpec.ts +368 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: refining-issues
|
|
3
|
+
description: Turns a backlog issue in a night-shift repository into one that an unattended agent can implement, and turns a coupled set into a chain that stacks. Use when auditing whether an issue is ready for the queue label, when grouping issues for planning, when an issue's acceptance ends in an unmade decision, when two issues must land together, when an issue asks for a test that guards a redundancy rather than removing it or manufactures one by quoting a document, or when a change to an authority document has just changed how a set of issues is partitioned. Encodes eight checks — five that decide readiness and three that decide independence — plus the five moves that fix a failed check.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Refining issues
|
|
7
|
+
|
|
8
|
+
An issue is finished refining when an agent holding no context but the issue
|
|
9
|
+
body can implement it, and when landing it leaves the tree in a state somebody
|
|
10
|
+
would ship.
|
|
11
|
+
|
|
12
|
+
That is the bar the queue label asserts. `night-shift-worker` is what consumes
|
|
13
|
+
it: a scheduled agent reads the issue, the documents it cites, and works it with
|
|
14
|
+
nobody watching. Refining is what makes that possible. Applying the label is the
|
|
15
|
+
maintainer's call, never yours.
|
|
16
|
+
|
|
17
|
+
**Read `.night-shift/config.md` before you refine anything.** Three of its
|
|
18
|
+
headings decide how these checks run: `## Issue label` names the queue this
|
|
19
|
+
repository sweeps, `## Authority` names the documents an issue answers to and
|
|
20
|
+
what citing each one means, and `## Before you fix` says what the night shift
|
|
21
|
+
accepts as warrant. A missing required heading is a stop here for the same
|
|
22
|
+
reason it is a stop for the worker — say which one you could not find, and
|
|
23
|
+
refine nothing.
|
|
24
|
+
|
|
25
|
+
Eight checks decide it. Run R1 to R5 against one issue. Run I1 to I3 across a
|
|
26
|
+
set. Each failed check has one move.
|
|
27
|
+
|
|
28
|
+
Do not rewrite an issue that passes. A well-argued problem statement that
|
|
29
|
+
already carries criteria that can be shown false is done, and adding structure
|
|
30
|
+
to it costs review attention for nothing.
|
|
31
|
+
|
|
32
|
+
## Readiness — run against one issue
|
|
33
|
+
|
|
34
|
+
### R1. Criteria
|
|
35
|
+
|
|
36
|
+
Does the issue state acceptance criteria, and can each one be shown false?
|
|
37
|
+
|
|
38
|
+
A criterion names an observable change. "The importer refuses a row whose date
|
|
39
|
+
is not ISO-8601, naming the row and the value" is a criterion. "Add date
|
|
40
|
+
validation to the importer" is a task.
|
|
41
|
+
|
|
42
|
+
What shows a criterion false depends on what the issue delivers.
|
|
43
|
+
|
|
44
|
+
- **Behavior in code.** A test that drives the behavior, and can fail when
|
|
45
|
+
the behavior is wrong.
|
|
46
|
+
- **A document or a declaration.** A check that does not restate the text,
|
|
47
|
+
and can fail when the document is wrong. The check derives its expectation from the code the document
|
|
48
|
+
describes, runs the document's examples through the parser or compiler, or
|
|
49
|
+
reads an outcome in use, such as a live run whose recorded cost disagrees
|
|
50
|
+
with the declared one.
|
|
51
|
+
- **A document with no such check.** A reviewer reads a stated property of the
|
|
52
|
+
page and sees whether it holds. "The section shows the token counts and the
|
|
53
|
+
prices it multiplies" is a property. "The rate is derived correctly" names a
|
|
54
|
+
method, so no reader can see it fail.
|
|
55
|
+
|
|
56
|
+
Do not add a test to make a criterion pass R1. A test that fails only when
|
|
57
|
+
somebody edits the text it asserts on shows nothing false, so it cannot be what
|
|
58
|
+
satisfies a criterion, and `night-shift-worker` holds its regression tests to
|
|
59
|
+
the same bar. An acceptance that asks for one fails R5.
|
|
60
|
+
|
|
61
|
+
Fail R1 when the acceptance section is absent, or when it is a checkbox list of
|
|
62
|
+
work items, or when a bullet reads "resolved by a decision about X". That last
|
|
63
|
+
form is the most common miss, because a decision sitting in the criterion slot
|
|
64
|
+
looks like a criterion.
|
|
65
|
+
|
|
66
|
+
### R2. Capability
|
|
67
|
+
|
|
68
|
+
Does the issue say what a caller can do after it lands that they cannot do now?
|
|
69
|
+
|
|
70
|
+
Read the acceptance and answer the question out loud. If the answer is a
|
|
71
|
+
restatement of the tasks, the issue describes work rather than an outcome.
|
|
72
|
+
|
|
73
|
+
R1 and R2 fail independently. An issue can carry twelve well-formed checkboxes
|
|
74
|
+
and still never say what changes.
|
|
75
|
+
|
|
76
|
+
### R3. Premise
|
|
77
|
+
|
|
78
|
+
If the issue claims a caller *cannot* do something, find the mechanism in code
|
|
79
|
+
before you accept the framing.
|
|
80
|
+
|
|
81
|
+
An issue that opens with a capability gap and then offers directions has often
|
|
82
|
+
not proved the gap. Search the project's own user-facing documentation first —
|
|
83
|
+
its README and whatever `## Read first` names — because a capability that exists
|
|
84
|
+
is usually documented there already. Then read the source.
|
|
85
|
+
|
|
86
|
+
A wrong premise does not produce a wrong answer. It produces a correct answer
|
|
87
|
+
to the wrong question, and every direction under it reads as reasonable. The
|
|
88
|
+
cost is a large fix where a small one was needed.
|
|
89
|
+
|
|
90
|
+
**A decision already recorded is not a gap.** Where an authority document
|
|
91
|
+
records decisions taken, or tensions accepted as compromises, an issue whose
|
|
92
|
+
premise is "the project should not have decided X" is arguing with that record
|
|
93
|
+
rather than reporting a defect. That is a maintainer's question, not a slice:
|
|
94
|
+
say so and stop. What survives such an issue is whatever observable defect it
|
|
95
|
+
noticed in passing, scoped down to that.
|
|
96
|
+
|
|
97
|
+
### R4. Anchor
|
|
98
|
+
|
|
99
|
+
Does the issue cite the documents `## Authority` names, in the form that
|
|
100
|
+
heading describes?
|
|
101
|
+
|
|
102
|
+
The night shift treats the issue body as an index into those documents, not as
|
|
103
|
+
a substitute for them. Authority usually comes in two roles, and a repository
|
|
104
|
+
may declare either, both, or neither:
|
|
105
|
+
|
|
106
|
+
- **What was decided.** A specification, an accepted design, an RFC. An issue
|
|
107
|
+
names the sections it answers to. Without them the agent guesses which
|
|
108
|
+
requirement it is implementing.
|
|
109
|
+
- **What decisions answer to.** A constitution, a set of principles, a review
|
|
110
|
+
rubric. An issue carries conformance criteria citing the ones each criterion
|
|
111
|
+
answers to. Without them a change can pass every test and still offend the
|
|
112
|
+
principle the repository holds itself to.
|
|
113
|
+
|
|
114
|
+
Fail R4 when the issue omits something `## Authority` requires. The fix is to
|
|
115
|
+
read the document and supply the citation, not to invent a requirement. Where no
|
|
116
|
+
section covers the behavior the issue wants, the issue is asking to amend the
|
|
117
|
+
document — which is the maintainer's decision, by the route `## Authority`
|
|
118
|
+
names, and never an implementation slice's to take.
|
|
119
|
+
|
|
120
|
+
**Where `## Authority` says `none`, R4 is vacuous. Record it as passed and say
|
|
121
|
+
why.** A repository that holds its issues to no document is a repository where
|
|
122
|
+
this check has nothing to read, which is not the same as an issue that skipped
|
|
123
|
+
it. R5 still runs: it does not depend on R4's documents.
|
|
124
|
+
|
|
125
|
+
### R5. Remedy
|
|
126
|
+
|
|
127
|
+
Does the issue prescribe a remedy that removes the defect, or a guard that
|
|
128
|
+
stands over it?
|
|
129
|
+
|
|
130
|
+
Where `## Authority` names principles, R5 also asks whether the requirement
|
|
131
|
+
obeys the one the issue cited. That is independent of R4: an issue can cite a
|
|
132
|
+
principle correctly and then ask for precisely what it diagnoses. Where
|
|
133
|
+
`## Authority` says `none`, R5 still runs on the reasoning below, which needs no
|
|
134
|
+
document behind it.
|
|
135
|
+
|
|
136
|
+
The shape is a value written in more than one place, and an acceptance that asks
|
|
137
|
+
for a test asserting the copies agree. The redundancy is the defect. A test over
|
|
138
|
+
the copies leaves every copy standing, adds one more thing to maintain, and goes
|
|
139
|
+
red only when somebody edits one of them — so it converts a problem you could
|
|
140
|
+
have removed into a problem you now have to keep. The remedy is not a check that
|
|
141
|
+
rejects the disagreement; it is an arrangement in which the copies cannot
|
|
142
|
+
disagree, because there is only one.
|
|
143
|
+
|
|
144
|
+
Ask which copy is the source of truth, and whether the rest can be derived or
|
|
145
|
+
deleted. When they can, that deletion is the requirement, and the test that
|
|
146
|
+
would have guarded them has nothing left to compare.
|
|
147
|
+
|
|
148
|
+
**The shape has a second form, and it is the one that slips through.** Sometimes
|
|
149
|
+
the fact is written in exactly one place, and the acceptance asks for a test
|
|
150
|
+
that quotes it: "a test reads the built declaration and finds `byte-stable`", "a
|
|
151
|
+
test asserts the page names both rejection types". No pre-existing redundancy
|
|
152
|
+
exists to point at, so the first form does not match. The redundancy arrives
|
|
153
|
+
with the test. The criterion underneath is usually sound — a property of the
|
|
154
|
+
page that a reviewer can see hold or fail, which is what R1 asks of a document.
|
|
155
|
+
Only the test is the copy.
|
|
156
|
+
|
|
157
|
+
Both forms answer one question: what could this test go red for? When the answer
|
|
158
|
+
is "somebody reworded the thing it quotes", the acceptance is a copy — whether
|
|
159
|
+
it guards a redundancy that was already there or manufactures one that was not.
|
|
160
|
+
|
|
161
|
+
Fail R5 when the acceptance's subject is agreement between two representations
|
|
162
|
+
of one fact — a runtime validation, a lint, a CI step, or a test — rather than
|
|
163
|
+
the removal of one of them. Fail it too when the acceptance asks for a test
|
|
164
|
+
whose only possible failure is an edit to the text it quotes, even where that
|
|
165
|
+
text is the only copy in the tree. The move is **Remove the freedom** for both,
|
|
166
|
+
though the second form has less to remove.
|
|
167
|
+
|
|
168
|
+
Run R5 after R4 where `## Authority` names principles, because the cited one is
|
|
169
|
+
part of what R5 reads: an issue that fails R4 offers nothing to check the
|
|
170
|
+
requirement against. Where it says `none`, the order does not matter.
|
|
171
|
+
|
|
172
|
+
## Independence — run across a set
|
|
173
|
+
|
|
174
|
+
### I1. Reference
|
|
175
|
+
|
|
176
|
+
Do this issue's acceptance criteria name another issue's outcome?
|
|
177
|
+
|
|
178
|
+
That is the whole test. Two issues that edit the same file are independent when
|
|
179
|
+
neither one's criteria mention the other. Two issues that share no file are
|
|
180
|
+
coupled when one's check is satisfiable while the other's hole is open.
|
|
181
|
+
|
|
182
|
+
Coupling lives in shared invariants. A shared file is not coupling.
|
|
183
|
+
|
|
184
|
+
When the answer is yes, the coupling has to reach the issue body as a *Depends
|
|
185
|
+
on* line. The night shift sequences off that declaration alone and does not
|
|
186
|
+
infer order from the files an issue touches, so an undeclared dependency is
|
|
187
|
+
invisible to it: two sessions take the coupled pair, both branch from `main`,
|
|
188
|
+
and produce conflicting patches for one design. A declared one becomes a stack.
|
|
189
|
+
|
|
190
|
+
The reverse costs too. A *Depends on* that no criterion needs makes a free issue
|
|
191
|
+
wait for a blocked one, and the night shift will honor it.
|
|
192
|
+
|
|
193
|
+
### I2. Dimension
|
|
194
|
+
|
|
195
|
+
Does the code this issue reads conflate the axis it cares about with another?
|
|
196
|
+
|
|
197
|
+
An issue inherits the dimensionality of what it reads. A predicate that ORs two
|
|
198
|
+
independent axes into one boolean forces every issue touching it to depend on
|
|
199
|
+
both, whether or not it cares about both. The dependency sits in the code rather
|
|
200
|
+
than in the work.
|
|
201
|
+
|
|
202
|
+
Hand-maintained consistency across two sites is one degree of freedom wearing
|
|
203
|
+
the costume of two, and the ripple it produces is coupling. An issue that fails
|
|
204
|
+
I2 has found surplus freedom in the representation. Where `## Authority` names
|
|
205
|
+
principles, saying which one it offends is usually the clearest statement of the
|
|
206
|
+
fix.
|
|
207
|
+
|
|
208
|
+
Fail I2 when the issue must wait for a decomposition rather than for a
|
|
209
|
+
behavior.
|
|
210
|
+
|
|
211
|
+
### I3. Intermediate
|
|
212
|
+
|
|
213
|
+
For each order of a coupled pair, is there a state worth shipping in between?
|
|
214
|
+
|
|
215
|
+
Write the criterion for the halfway point. Usually one order has a coherent
|
|
216
|
+
intermediate and the reverse does not, and that asymmetry is the ordering.
|
|
217
|
+
|
|
218
|
+
The danger this check catches is specific. Whichever issue of a coupled pair
|
|
219
|
+
lands first ships a green test suite asserting a partial invariant, and a
|
|
220
|
+
passing test that claims coverage it does not have goes into every committed
|
|
221
|
+
baseline as real signal.
|
|
222
|
+
|
|
223
|
+
Once the order is written down, the pair costs nothing in wall-clock: the upper
|
|
224
|
+
layer branches from the lower one's branch and opens a stacked pull request, so
|
|
225
|
+
neither waits for the other to merge. Ordering is what the night shift needs;
|
|
226
|
+
serialization is not.
|
|
227
|
+
|
|
228
|
+
## The five moves
|
|
229
|
+
|
|
230
|
+
**Extract a decision.** Several issues stalling on "Directions:" or "Decide
|
|
231
|
+
first" are often stalling on one decision in different words. Record it once,
|
|
232
|
+
outside all of them, in a design note kept wherever this repository keeps them.
|
|
233
|
+
Every issue that depended
|
|
234
|
+
on it becomes independent with no change to its scope. Answering separately
|
|
235
|
+
produces answers that need not agree. When the decision is one the spec already
|
|
236
|
+
owns — a default, a vocabulary choice, a division of responsibility — the note
|
|
237
|
+
is not yours to write: it is an amendment, and it lands by the route
|
|
238
|
+
`## Authority` names, by the maintainer.
|
|
239
|
+
|
|
240
|
+
**Remove the freedom.** When R5 fails, name the source of truth and rewrite the
|
|
241
|
+
acceptance as the deletion or derivation of every other copy. The guard the
|
|
242
|
+
issue asked for goes with them: with one statement left there is nothing to
|
|
243
|
+
compare. The guard does not need removing separately; it becomes vacuous.
|
|
244
|
+
|
|
245
|
+
Write two consequences into the issue. Say that no test asserting agreement is
|
|
246
|
+
to be added, because an agent reaching for evidence will otherwise supply one
|
|
247
|
+
and restore the freedom the issue just removed. And where one of the copies
|
|
248
|
+
lives in a document `## Authority` names, deleting it is an amendment, so the
|
|
249
|
+
issue has to declare itself one in its title and its acceptance. The night shift
|
|
250
|
+
stops and asks rather than guessing which kind of issue it holds.
|
|
251
|
+
|
|
252
|
+
Where the acceptance manufactures the copy rather than guarding one, there is
|
|
253
|
+
nothing to delete and the move is smaller: strike the test and keep the
|
|
254
|
+
criterion as a property a reviewer checks, or replace the test with one that
|
|
255
|
+
derives. A derived criterion names something the *code* can
|
|
256
|
+
falsify — a documented example that compiles against the shipped build, a
|
|
257
|
+
documented path that resolves against the filesystem, a default read back out of
|
|
258
|
+
the object the factory built. Each fails when the code changes, which is the
|
|
259
|
+
failure a document cannot produce on its own.
|
|
260
|
+
|
|
261
|
+
Where nothing derives, say so in the issue: the documentation stands on review,
|
|
262
|
+
and no test is to be added. That sentence is load-bearing for the same reason it
|
|
263
|
+
is in the first form. An agent held to a regression test, reading an acceptance
|
|
264
|
+
that names a sentence, will quote the sentence back.
|
|
265
|
+
|
|
266
|
+
**Decompose the axis.** When I2 fails, the fix belongs to whichever issue owns
|
|
267
|
+
the separation rather than to the issue that noticed it. Decomposing the predicate is
|
|
268
|
+
what makes the other issues perpendicular, so it is scope for the owner rather
|
|
269
|
+
than a new issue.
|
|
270
|
+
|
|
271
|
+
**Define the intermediate criterion.** When I3 finds a coherent halfway state,
|
|
272
|
+
give it to the issue that lands first. This converts a must-land-together pair
|
|
273
|
+
into a sequence, and it moves the shared invariant out of the gap and into an
|
|
274
|
+
issue that owns it. A note reading "whichever lands second must not reopen this"
|
|
275
|
+
is prose, and prose does not run.
|
|
276
|
+
|
|
277
|
+
**Split.** An issue holding more than one concern splits, even when the parts
|
|
278
|
+
share a file. Prefer the split when one part is unblocked today and another
|
|
279
|
+
waits on a dependency, because bundling makes the free part wait.
|
|
280
|
+
|
|
281
|
+
## Worked example
|
|
282
|
+
|
|
283
|
+
`references/worked-example.md` runs R1 to R3, R5, and I1 to I3 plus both
|
|
284
|
+
structural moves over one backlog, and closes each with the shape to recognize. The domain
|
|
285
|
+
is invented so nothing in it decays as a real backlog is worked. Read it when a
|
|
286
|
+
check's description is not enough to recognize what is in front of you.
|
|
287
|
+
|
|
288
|
+
R4 has no shape section because it is a presence check: the sections are named
|
|
289
|
+
or they are not.
|
|
290
|
+
|
|
291
|
+
## Writing the refined issue
|
|
292
|
+
|
|
293
|
+
Write the title as the defect, then "so", then the consequence. "`start()` has
|
|
294
|
+
no bound, so an unreachable server holds a boot path open."
|
|
295
|
+
|
|
296
|
+
Body leads with what happened or what the mechanism is, cites the code by path
|
|
297
|
+
and line, and closes with acceptance. A serviceable order: the mechanism in
|
|
298
|
+
prose; **Measured** or **Demonstrated**, showing the behavior against something
|
|
299
|
+
real; **Build** or **Proposal**; **Tests**; **Conformance**, naming what each
|
|
300
|
+
criterion answers to; **Depends on**. Drop **Conformance** where `## Authority`
|
|
301
|
+
says `none` — a heading with nothing to cite is a heading that gets filled in
|
|
302
|
+
with something invented.
|
|
303
|
+
|
|
304
|
+
A documentation slice may carry no **Tests** entry at all: its criteria are
|
|
305
|
+
properties a reviewer checks, which R1 accepts. Every entry that is there has to
|
|
306
|
+
be able to fail for a reason other than an edit to its own subject. This is the
|
|
307
|
+
sentence R5 most often catches too late: a documentation slice feels like it
|
|
308
|
+
needs a test, a quotation is the only one available, and writing it here is what
|
|
309
|
+
puts it in the suite. The night shift implements the acceptance it is given.
|
|
310
|
+
|
|
311
|
+
Name the interaction with any issue that shares an invariant, and say which one
|
|
312
|
+
must not reopen the gap.
|
|
313
|
+
|
|
314
|
+
Say plainly what the issue no longer claims when a check removed part of it. A
|
|
315
|
+
reader who remembers the old framing needs to know it is gone, and the removed
|
|
316
|
+
argument does not belong in the body. This is `CLAUDE.md`'s no-reversed-decision
|
|
317
|
+
rule applied to a tracker: the refined issue describes only what is wrong now.
|
|
318
|
+
|
|
319
|
+
Refining destroys the evidence. An issue that passes the checks no longer shows
|
|
320
|
+
the defect that made it fail them, and a tracker keeps no body history a later
|
|
321
|
+
reader can reach. GitHub's timeline API returns `renamed` and `labeled` events
|
|
322
|
+
and never the previous body.
|
|
323
|
+
|
|
324
|
+
So an issue is a poor place to point somebody who needs to learn a shape. It is
|
|
325
|
+
also a decaying one, because the code a good example quotes is usually the code
|
|
326
|
+
the issue deletes. Teach from a constructed case that holds the shape, and keep
|
|
327
|
+
the issue number as provenance for a reader who wants the original.
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
# Worked example
|
|
2
|
+
|
|
3
|
+
One backlog. The project is a deployment CLI: it builds an artifact, deploys it
|
|
4
|
+
to staging, and promotes a staging build to production.
|
|
5
|
+
It has an auth check on its deploy API, a `--dry-run` flag, and a `help`
|
|
6
|
+
command.
|
|
7
|
+
|
|
8
|
+
The domain is invented so the shapes stay legible outside any one codebase.
|
|
9
|
+
The shapes are not. See Provenance at the end.
|
|
10
|
+
|
|
11
|
+
## R1 Criteria — a decision in the criterion slot
|
|
12
|
+
|
|
13
|
+
An issue lists four confusing error messages and closes with acceptance. Three
|
|
14
|
+
bullets can be shown false. The fourth:
|
|
15
|
+
|
|
16
|
+
> - Case 4 is resolved by a decision about which registry format the artifact
|
|
17
|
+
> digest uses, and the `help` text matches that decision.
|
|
18
|
+
|
|
19
|
+
It has the grammar of a criterion. It names no observable change and cannot be
|
|
20
|
+
shown false, because what it requires is that somebody choose. Three quarters
|
|
21
|
+
of the issue is implementable and the fourth quarter is a question wearing a
|
|
22
|
+
criterion's clothes.
|
|
23
|
+
|
|
24
|
+
**The shape:** an acceptance bullet whose verb is *decide*, *resolve*,
|
|
25
|
+
*determine*, or *clarify*.
|
|
26
|
+
|
|
27
|
+
## R2 Capability — a task list that never states an outcome
|
|
28
|
+
|
|
29
|
+
An issue about unvalidated deploy manifests carries eleven checkboxes across
|
|
30
|
+
design, implementation, and rollout, plus a stated fix ordering. It reads as
|
|
31
|
+
the most complete issue in the backlog. One bullet:
|
|
32
|
+
|
|
33
|
+
> - [ ] Add `validateManifest` to `src/deploy/manifest.ts`, sibling to
|
|
34
|
+
> `loadManifest`: walk the parsed tree and check every referenced image tag
|
|
35
|
+
> resolves in the registry.
|
|
36
|
+
|
|
37
|
+
That is a precise task. Nowhere does the issue say what an operator can do
|
|
38
|
+
afterward that they cannot do now. The criterion it lacks is one sentence:
|
|
39
|
+
*deploy refuses a manifest that references an image tag the registry does not
|
|
40
|
+
hold, naming the tag.*
|
|
41
|
+
|
|
42
|
+
**The shape:** an issue you could hand to an implementer that leaves a reviewer
|
|
43
|
+
unable to say what changed. R1 passes, R2 fails, and the two fail
|
|
44
|
+
independently.
|
|
45
|
+
|
|
46
|
+
## R3 Premise — a capability gap that was not there
|
|
47
|
+
|
|
48
|
+
An issue titled "An operator cannot see what a deploy will change without
|
|
49
|
+
running it" opens:
|
|
50
|
+
|
|
51
|
+
> This issue is a request for a strategy rather than a patch, because the two
|
|
52
|
+
> candidate patches pull against each other.
|
|
53
|
+
|
|
54
|
+
and offers four directions, the largest of which restructures how the CLI holds
|
|
55
|
+
a plan.
|
|
56
|
+
|
|
57
|
+
The premise is false. `deploy --dry-run` prints the plan and exits, and
|
|
58
|
+
`help deploy` documents it. All four directions solve a problem that does not
|
|
59
|
+
exist.
|
|
60
|
+
|
|
61
|
+
One real defect the issue mentions in passing survives: the dry-run output
|
|
62
|
+
omits image digests, so an operator cannot tell two builds of the same tag
|
|
63
|
+
apart. The issue scopes down to that.
|
|
64
|
+
|
|
65
|
+
**The shape:** an issue that opens with "the caller cannot X" and proceeds to
|
|
66
|
+
directions without ever showing the code that refuses. Check the product's own
|
|
67
|
+
help text before the handler, because a capability that exists is usually
|
|
68
|
+
documented there already.
|
|
69
|
+
|
|
70
|
+
## R5 Remedy — a guard where the freedom should have been removed
|
|
71
|
+
|
|
72
|
+
The promote timeout is written in three places: `help promote` prints it in
|
|
73
|
+
prose, `config/defaults.yml` sets it, and the deploy API client hard-codes the
|
|
74
|
+
same number as its own fallback. Nothing ties them together, and they agree
|
|
75
|
+
today by hand. An issue notices this and closes with:
|
|
76
|
+
|
|
77
|
+
> - A test reads the timeout from all three and asserts they agree.
|
|
78
|
+
|
|
79
|
+
The criterion is observable and can be shown false, so R1 passes. It names an
|
|
80
|
+
outcome, so R2 passes. The drift is real and demonstrated, so R3 passes. It cites
|
|
81
|
+
the principle its repository holds duplication to, so R4 passes. And it asks for
|
|
82
|
+
exactly what that principle diagnoses.
|
|
83
|
+
|
|
84
|
+
Three independent variables stand where the problem has one, and the acceptance
|
|
85
|
+
adds a fourth thing to maintain rather than removing two. The remedy is to name
|
|
86
|
+
`config/defaults.yml` the source, have the client read it instead of carrying a
|
|
87
|
+
fallback, and have `help promote` print the value instead of spelling it. Then
|
|
88
|
+
no test has anything left to compare.
|
|
89
|
+
|
|
90
|
+
**The shape:** an acceptance whose subject is agreement between copies. The
|
|
91
|
+
issue has found surplus freedom and asked for a guard to stand over it.
|
|
92
|
+
|
|
93
|
+
## R5 Remedy — a copy the acceptance creates
|
|
94
|
+
|
|
95
|
+
`promote --wait` blocks until the deploy settles, and its help text does not say
|
|
96
|
+
that cancelling the wait leaves the deploy running. An issue adds the sentence
|
|
97
|
+
and closes with:
|
|
98
|
+
|
|
99
|
+
> - `promote --wait`'s help text says that cancelling the wait does not cancel
|
|
100
|
+
> the deploy.
|
|
101
|
+
> - A test reads `promote --help` and asserts it contains that sentence.
|
|
102
|
+
|
|
103
|
+
Nothing is written twice here. The help text is the only place that fact lives,
|
|
104
|
+
and the issue is right that it belongs there. The first bullet is a criterion,
|
|
105
|
+
a property a reviewer can see hold or fail, so R1 passes. It names an outcome, so R2
|
|
106
|
+
passes. An operator really did cancel a wait and assume the deploy stopped, so
|
|
107
|
+
R3 passes. The principle about keeping an explanation in one place is cited,
|
|
108
|
+
correctly, so R4 passes.
|
|
109
|
+
|
|
110
|
+
The second bullet is not a criterion about the product at all. It prescribes a
|
|
111
|
+
test, and the test is the copy that arrives with the acceptance. It can go red
|
|
112
|
+
for one reason: somebody rewords the sentence. It cannot notice that cancelling
|
|
113
|
+
now *does* cancel the deploy, which is the only thing worth knowing. It freezes
|
|
114
|
+
the wording and leaves the behavior unguarded.
|
|
115
|
+
|
|
116
|
+
What derives here is the flag, not the sentence. A check that every flag the
|
|
117
|
+
parser registers appears in `--help`, and that nothing else does, fails when
|
|
118
|
+
somebody adds or renames a flag without documenting it — a failure the code
|
|
119
|
+
produces. The refined acceptance keeps the criterion, strikes the test, and
|
|
120
|
+
says no test is to be added for the sentence: it stands on review.
|
|
121
|
+
|
|
122
|
+
**The shape:** an acceptance that quotes a document back to itself. The first
|
|
123
|
+
form guards a redundancy that exists; this one manufactures one that did not,
|
|
124
|
+
which is why reading for surplus freedom does not find it.
|
|
125
|
+
|
|
126
|
+
## I1 Reference — a shared invariant across no shared file
|
|
127
|
+
|
|
128
|
+
Issue A adds an authorization check to the deploy API route. Issue B removes an
|
|
129
|
+
internal scheduler that calls the deploy handler directly, bypassing the route.
|
|
130
|
+
|
|
131
|
+
They edit different files. They are not independent. A's criteria are all
|
|
132
|
+
satisfiable while the scheduler still reaches the handler, so an unauthorized
|
|
133
|
+
deploy still happens and A's tests are green.
|
|
134
|
+
|
|
135
|
+
**The shape:** two issues whose criteria are each complete and whose
|
|
136
|
+
conjunction is the actual invariant. Neither body has to mention the other for
|
|
137
|
+
the coupling to be real, which is why the check reads the criteria rather than
|
|
138
|
+
the file list.
|
|
139
|
+
|
|
140
|
+
## I2 Dimension — waiting on a decomposition rather than a behavior
|
|
141
|
+
|
|
142
|
+
An issue makes `status` report whether the working tree is safe to switch
|
|
143
|
+
branches. Its first draft:
|
|
144
|
+
|
|
145
|
+
> - The answer is derived from the state `status` already reports. No new
|
|
146
|
+
> stored field.
|
|
147
|
+
|
|
148
|
+
The second sentence is right and the first is wrong. `status` reports one
|
|
149
|
+
boolean that ORs two independent axes:
|
|
150
|
+
|
|
151
|
+
```
|
|
152
|
+
pending = hasUncommittedEdits() || hasUnpushedCommits()
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Branch safety depends on the first alone, and the disjunction loses it in
|
|
156
|
+
exactly the ordinary case:
|
|
157
|
+
|
|
158
|
+
| `pending` | unpushed | uncommitted edits |
|
|
159
|
+
|---|---|---|
|
|
160
|
+
| false | any | false |
|
|
161
|
+
| true | 0 | true |
|
|
162
|
+
| true | above 0 | unknown |
|
|
163
|
+
|
|
164
|
+
The third row is the common one. So the dependency is not on another behavior.
|
|
165
|
+
It is on a predicate being split along the axis this issue cares about, and
|
|
166
|
+
that split belongs to whichever issue owns separating the two.
|
|
167
|
+
|
|
168
|
+
**The shape:** an issue that cannot be stated cleanly because a value it must
|
|
169
|
+
read conflates two things. The dependency sits in the code rather than in the
|
|
170
|
+
work.
|
|
171
|
+
|
|
172
|
+
## I3 Intermediate — the order falls out of the halfway state
|
|
173
|
+
|
|
174
|
+
Take the coupled pair from I1 and write the criterion for each order.
|
|
175
|
+
|
|
176
|
+
**B first.** The intermediate is *every deploy path goes through the route*,
|
|
177
|
+
true by removal alone. Worth shipping on its own. A then adds *the route
|
|
178
|
+
authorizes the caller*.
|
|
179
|
+
|
|
180
|
+
**A first.** The intermediate is *the route authorizes, except the path that
|
|
181
|
+
skips it*. Not worth shipping, and worse than not shipping, because it lands a
|
|
182
|
+
green test suite over an open hole and that green goes into every later
|
|
183
|
+
baseline as real signal.
|
|
184
|
+
|
|
185
|
+
Only one order has a coherent halfway state. That asymmetry is the ordering.
|
|
186
|
+
|
|
187
|
+
## Split — one issue, three concerns
|
|
188
|
+
|
|
189
|
+
One issue holds four confusing error messages and two stale help topics. Sorted
|
|
190
|
+
by what each part actually is:
|
|
191
|
+
|
|
192
|
+
- Three message fixes, extending a pattern that already exists, blocked on
|
|
193
|
+
nothing.
|
|
194
|
+
- One format change with a compatibility rationale, coupled to another issue
|
|
195
|
+
under I1.
|
|
196
|
+
- Two help topics that are stale because a newer flag replaced what they
|
|
197
|
+
describe, wrong today for a reason unrelated to either.
|
|
198
|
+
|
|
199
|
+
Three issues. The message fixes stop waiting on a decision that is not theirs,
|
|
200
|
+
and the help fix stops waiting on both.
|
|
201
|
+
|
|
202
|
+
**The shape:** a single issue whose parts have different blockers. Bundling
|
|
203
|
+
makes the free part wait for the blocked one.
|
|
204
|
+
|
|
205
|
+
## Extract — four issues, one decision
|
|
206
|
+
|
|
207
|
+
Four issues each end without a decision:
|
|
208
|
+
|
|
209
|
+
- Which component owns retry policy for a failed deploy?
|
|
210
|
+
- Which component owns retry policy for a failed registry push?
|
|
211
|
+
- Should a partial rollout retry the failed shards or the whole batch?
|
|
212
|
+
- Where does the retry budget live?
|
|
213
|
+
|
|
214
|
+
Four phrasings of one question: which layer owns retry, and against what
|
|
215
|
+
budget. Recording the answer once, in a design note outside all four, makes
|
|
216
|
+
every one of them independent without changing any of their scope. Answered
|
|
217
|
+
separately, they produce four answers that need not agree.
|
|
218
|
+
|
|
219
|
+
## Provenance
|
|
220
|
+
|
|
221
|
+
Every shape here was found in a real backlog audit of `jinaga/factual-mcp` in
|
|
222
|
+
September 2026, across issues 351, 373, 377, 404, 405, 406, 412, 415, and 416.
|
|
223
|
+
The R5 shapes came from `jinaga/jinaga-worker`: the first from #52 in the same
|
|
224
|
+
month, the second from #19, #22 and #39, whose acceptance sections each asked
|
|
225
|
+
for a test quoting a document back to itself and got one.
|
|
226
|
+
|
|
227
|
+
The real cases are not reproduced, for two reasons. They carry domain
|
|
228
|
+
vocabulary that costs a reader more than the shape is worth. And each one
|
|
229
|
+
quotes code that the issue describing it deletes, so a faithful transcript
|
|
230
|
+
starts decaying the moment the backlog is worked.
|
|
231
|
+
|
|
232
|
+
Read the issues if you want the originals. Expect them to have been refined,
|
|
233
|
+
and expect no previous body to be reachable: a tracker keeps no history of an
|
|
234
|
+
issue's text, so what made an issue fail a check is gone once it passes.
|
|
@@ -4,11 +4,11 @@ name: CI
|
|
|
4
4
|
|
|
5
5
|
# Controls when the workflow will run
|
|
6
6
|
on:
|
|
7
|
-
# Triggers
|
|
7
|
+
# Triggers on pushes to main, and on every pull request whatever its base,
|
|
8
|
+
# so each layer of a stacked chain gets its own check runs
|
|
8
9
|
push:
|
|
9
10
|
branches: [ main ]
|
|
10
11
|
pull_request:
|
|
11
|
-
branches: [ main ]
|
|
12
12
|
|
|
13
13
|
# Allows you to run this workflow manually from the Actions tab
|
|
14
14
|
workflow_dispatch:
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Night shift configuration
|
|
2
|
+
|
|
3
|
+
The `night-shift-worker`, `night-shift-coordinator`, and `refining-issues`
|
|
4
|
+
skills read this file. Every `##` heading below except `## Branch prefix` is
|
|
5
|
+
required. A missing heading is an error, not a default: stop and name the
|
|
6
|
+
heading you could not find. Anything that is not one of these headings is
|
|
7
|
+
protocol, and protocol lives in the skill.
|
|
8
|
+
|
|
9
|
+
## Visibility
|
|
10
|
+
|
|
11
|
+
`public`
|
|
12
|
+
|
|
13
|
+
There is nothing to withhold here. One sibling repository in this practice is
|
|
14
|
+
private, so never carry its contents into an issue or pull request in this one.
|
|
15
|
+
|
|
16
|
+
## Issue label
|
|
17
|
+
|
|
18
|
+
`ready`
|
|
19
|
+
|
|
20
|
+
## Read first
|
|
21
|
+
|
|
22
|
+
- `CLAUDE.md` — commands, layers, storage implementations, and where the
|
|
23
|
+
Specification-to-SQL translation lives.
|
|
24
|
+
- The documents under `## Authority`, at the articles the issue cites.
|
|
25
|
+
|
|
26
|
+
## Authority
|
|
27
|
+
|
|
28
|
+
- `.claude/skills/degrees-of-freedom/degrees-of-freedom-constitution.md` — the
|
|
29
|
+
principles a design answers to. An issue's **Conformance** section cites the
|
|
30
|
+
article each criterion answers to. An amendment to it is the maintainer's
|
|
31
|
+
decision and never lands in an implementation slice.
|
|
32
|
+
|
|
33
|
+
## Before you fix
|
|
34
|
+
|
|
35
|
+
Reproduce first. An issue's repro may have been reconstructed rather than
|
|
36
|
+
verified by its reporter, so a repro that fails is a finding, not a failure.
|
|
37
|
+
Report it and open no pull request.
|
|
38
|
+
|
|
39
|
+
Much of this server's behavior comes from the `jinaga` library it depends on.
|
|
40
|
+
A defect may live there rather than here, and may already be fixed in a newer
|
|
41
|
+
`jinaga` release. Check which side of that boundary the defect is on before
|
|
42
|
+
changing code in this repository.
|
|
43
|
+
|
|
44
|
+
## Verification commands
|
|
45
|
+
|
|
46
|
+
Green before every push, in this order.
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
npm ci
|
|
50
|
+
npm run build
|
|
51
|
+
npm test
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The container starts with no `node_modules`, so `npm ci` is not optional, and a
|
|
55
|
+
stale `node_modules` fails the build against types the current `jinaga`
|
|
56
|
+
exports. `npm test` type-checks and runs Jest without a database.
|
|
57
|
+
|
|
58
|
+
`npm run integration` needs Docker and PostgreSQL and is not part of the bar.
|
|
59
|
+
|
|
60
|
+
## CI workflow
|
|
61
|
+
|
|
62
|
+
`main.yml`
|
|
63
|
+
|
|
64
|
+
Read runs for this file; it is the merge gate. It runs on a bare
|
|
65
|
+
`pull_request:` with no `branches:` filter, so every layer of a stack gets
|
|
66
|
+
check runs from its own pull request event.
|
|
67
|
+
|
|
68
|
+
`publish.yml` and `upgrade-jinaga.yml` do not gate a pull request. Address the
|
|
69
|
+
workflow by this file name rather than by its display name, `CI`.
|
|
70
|
+
|
|
71
|
+
## After the pull request
|
|
72
|
+
|
|
73
|
+
`none`
|
|
74
|
+
|
|
75
|
+
## What is different about this repository
|
|
76
|
+
|
|
77
|
+
This is a published npm package whose queue is mostly defects reported against
|
|
78
|
+
released versions. An issue may not reproduce at all, and saying so with
|
|
79
|
+
evidence is the whole result. Do not manufacture a fix to justify the session.
|
|
80
|
+
|
|
81
|
+
`upgrade-jinaga.yml` opens a pull request daily when a new `jinaga` is
|
|
82
|
+
published, on a branch named `upgrade/jinaga-<version>`. Those pull requests
|
|
83
|
+
are not night-shift work and are not prior work for an issue, unless the issue
|
|
84
|
+
is the upgrade itself.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Installed skills
|
|
2
|
+
|
|
3
|
+
These skills are installed here as committed copies. They are not fetched at run
|
|
4
|
+
time: an upstream edit would otherwise change what an unattended run does
|
|
5
|
+
without anyone reviewing it, and a network install adds a failure mode at
|
|
6
|
+
whatever hour the run wakes.
|
|
7
|
+
|
|
8
|
+
| Skill | Source | Upstream commit |
|
|
9
|
+
|---|---|---|
|
|
10
|
+
| `night-shift-worker` | `factoryengineering/skills` | `2fb85d8297c0a3059365bd809b8242f5657f2740` |
|
|
11
|
+
| `refining-issues` | `factoryengineering/skills` | `2fb85d8297c0a3059365bd809b8242f5657f2740` |
|
|
12
|
+
| `degrees-of-freedom` | `michaellperry/skills` | `3722c778facbfccc609a928d25ffb22e1ba461e6` |
|
|
13
|
+
| `night-shift-setup` | `factoryengineering/skills` | `2fb85d8297c0a3059365bd809b8242f5657f2740` |
|
|
14
|
+
|
|
15
|
+
To take an upstream change, re-copy the skill directory from its source, update
|
|
16
|
+
its commit above, and read the diff before you push. Local edits to an installed
|
|
17
|
+
skill belong upstream instead, or the next update silently reverts them.
|