felenova-backlog 0.1.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/README.md +58 -0
- package/bin/backlog.js +2 -0
- package/dist/_core/criteria.d.ts +13 -0
- package/dist/_core/criteria.js +44 -0
- package/dist/_core/criteria.js.map +1 -0
- package/dist/_core/errors.d.ts +53 -0
- package/dist/_core/errors.js +91 -0
- package/dist/_core/errors.js.map +1 -0
- package/dist/_core/index.d.ts +24 -0
- package/dist/_core/index.js +27 -0
- package/dist/_core/index.js.map +1 -0
- package/dist/_core/loop.d.ts +73 -0
- package/dist/_core/loop.js +46 -0
- package/dist/_core/loop.js.map +1 -0
- package/dist/_core/markdown/items.d.ts +35 -0
- package/dist/_core/markdown/items.js +256 -0
- package/dist/_core/markdown/items.js.map +1 -0
- package/dist/_core/markdown/matrix.d.ts +20 -0
- package/dist/_core/markdown/matrix.js +147 -0
- package/dist/_core/markdown/matrix.js.map +1 -0
- package/dist/_core/markdown/track.d.ts +71 -0
- package/dist/_core/markdown/track.js +79 -0
- package/dist/_core/markdown/track.js.map +1 -0
- package/dist/_core/matrix-status.d.ts +9 -0
- package/dist/_core/matrix-status.js +31 -0
- package/dist/_core/matrix-status.js.map +1 -0
- package/dist/_core/matrix.d.ts +46 -0
- package/dist/_core/matrix.js +0 -0
- package/dist/_core/matrix.js.map +1 -0
- package/dist/_core/permissions.d.ts +14 -0
- package/dist/_core/permissions.js +87 -0
- package/dist/_core/permissions.js.map +1 -0
- package/dist/_core/promote.d.ts +20 -0
- package/dist/_core/promote.js +47 -0
- package/dist/_core/promote.js.map +1 -0
- package/dist/_core/ready.d.ts +8 -0
- package/dist/_core/ready.js +22 -0
- package/dist/_core/ready.js.map +1 -0
- package/dist/_core/review.d.ts +49 -0
- package/dist/_core/review.js +137 -0
- package/dist/_core/review.js.map +1 -0
- package/dist/_core/schema.d.ts +636 -0
- package/dist/_core/schema.js +304 -0
- package/dist/_core/schema.js.map +1 -0
- package/dist/_core/ship.d.ts +23 -0
- package/dist/_core/ship.js +31 -0
- package/dist/_core/ship.js.map +1 -0
- package/dist/_core/spec-diff.d.ts +19 -0
- package/dist/_core/spec-diff.js +25 -0
- package/dist/_core/spec-diff.js.map +1 -0
- package/dist/_core/status.d.ts +7 -0
- package/dist/_core/status.js +25 -0
- package/dist/_core/status.js.map +1 -0
- package/dist/_core/util/deep-equal.d.ts +8 -0
- package/dist/_core/util/deep-equal.js +31 -0
- package/dist/_core/util/deep-equal.js.map +1 -0
- package/dist/api.d.ts +46 -0
- package/dist/api.js +104 -0
- package/dist/api.js.map +1 -0
- package/dist/cache.d.ts +53 -0
- package/dist/cache.js +114 -0
- package/dist/cache.js.map +1 -0
- package/dist/commands/common.d.ts +59 -0
- package/dist/commands/common.js +94 -0
- package/dist/commands/common.js.map +1 -0
- package/dist/commands/export.d.ts +20 -0
- package/dist/commands/export.js +56 -0
- package/dist/commands/export.js.map +1 -0
- package/dist/commands/matrix.d.ts +16 -0
- package/dist/commands/matrix.js +48 -0
- package/dist/commands/matrix.js.map +1 -0
- package/dist/commands/next.d.ts +14 -0
- package/dist/commands/next.js +30 -0
- package/dist/commands/next.js.map +1 -0
- package/dist/commands/promote.d.ts +15 -0
- package/dist/commands/promote.js +172 -0
- package/dist/commands/promote.js.map +1 -0
- package/dist/commands/report.d.ts +18 -0
- package/dist/commands/report.js +45 -0
- package/dist/commands/report.js.map +1 -0
- package/dist/commands/ship.d.ts +15 -0
- package/dist/commands/ship.js +231 -0
- package/dist/commands/ship.js.map +1 -0
- package/dist/commands/status.d.ts +13 -0
- package/dist/commands/status.js +141 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/sync.d.ts +17 -0
- package/dist/commands/sync.js +90 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/conductor/registry.d.ts +45 -0
- package/dist/conductor/registry.js +164 -0
- package/dist/conductor/registry.js.map +1 -0
- package/dist/config.d.ts +47 -0
- package/dist/config.js +137 -0
- package/dist/config.js.map +1 -0
- package/dist/doctor.d.ts +28 -0
- package/dist/doctor.js +377 -0
- package/dist/doctor.js.map +1 -0
- package/dist/errors.d.ts +23 -0
- package/dist/errors.js +114 -0
- package/dist/errors.js.map +1 -0
- package/dist/export.d.ts +1 -0
- package/dist/export.js +9 -0
- package/dist/export.js.map +1 -0
- package/dist/import.d.ts +3 -0
- package/dist/import.js +89 -0
- package/dist/import.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +183 -0
- package/dist/index.js.map +1 -0
- package/dist/init/auth.d.ts +85 -0
- package/dist/init/auth.js +194 -0
- package/dist/init/auth.js.map +1 -0
- package/dist/init/claudemd.d.ts +16 -0
- package/dist/init/claudemd.js +62 -0
- package/dist/init/claudemd.js.map +1 -0
- package/dist/init/index.d.ts +47 -0
- package/dist/init/index.js +159 -0
- package/dist/init/index.js.map +1 -0
- package/dist/init/mcp.d.ts +18 -0
- package/dist/init/mcp.js +97 -0
- package/dist/init/mcp.js.map +1 -0
- package/dist/init/plugin.d.ts +60 -0
- package/dist/init/plugin.js +120 -0
- package/dist/init/plugin.js.map +1 -0
- package/dist/init/scaffold.d.ts +19 -0
- package/dist/init/scaffold.js +46 -0
- package/dist/init/scaffold.js.map +1 -0
- package/dist/init/templates.d.ts +48 -0
- package/dist/init/templates.js +147 -0
- package/dist/init/templates.js.map +1 -0
- package/dist/plugin/.claude-plugin/plugin.json +11 -0
- package/dist/plugin/README.md +117 -0
- package/dist/plugin/commands/next.md +28 -0
- package/dist/plugin/commands/promote.md +30 -0
- package/dist/plugin/commands/report.md +26 -0
- package/dist/plugin/commands/ship.md +30 -0
- package/dist/plugin/skills/backlog-management/SKILL.md +63 -0
- package/dist/plugin/skills/backlog-query/SKILL.md +57 -0
- package/dist/plugin/skills/feature-kickoff/SKILL.md +112 -0
- package/dist/snapshot.d.ts +24 -0
- package/dist/snapshot.js +2 -0
- package/dist/snapshot.js.map +1 -0
- package/dist/token.d.ts +29 -0
- package/dist/token.js +99 -0
- package/dist/token.js.map +1 -0
- package/package.json +52 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ReasonRequiredError, TransitionError } from './errors.js';
|
|
2
|
+
/** §5.6: one-way, one step at a time; ⏭️ from any state with a reason. */
|
|
3
|
+
export const MATRIX_FORWARD = ['☐', '🟡', '✅', '🔬', '🚀'];
|
|
4
|
+
export function canTransitionMatrix(from, to) {
|
|
5
|
+
const f = from ?? '☐';
|
|
6
|
+
if (to === '⏭️')
|
|
7
|
+
return f !== '⏭️';
|
|
8
|
+
const i = MATRIX_FORWARD.indexOf(f);
|
|
9
|
+
const j = MATRIX_FORWARD.indexOf(to);
|
|
10
|
+
return i >= 0 && j === i + 1;
|
|
11
|
+
}
|
|
12
|
+
export function transitionMatrixRow(row, to, opts = {}) {
|
|
13
|
+
if (!canTransitionMatrix(row.status, to))
|
|
14
|
+
throw new TransitionError('matrix_status', row.status ?? '☐', to);
|
|
15
|
+
if (to === '⏭️') {
|
|
16
|
+
if (!opts.reason?.trim())
|
|
17
|
+
throw new ReasonRequiredError('skip');
|
|
18
|
+
const tag = `⏭️ ${opts.reason.trim()}`;
|
|
19
|
+
return { ...row, status: to, notes: row.notes ? `${row.notes} — ${tag}` : tag };
|
|
20
|
+
}
|
|
21
|
+
return { ...row, status: to };
|
|
22
|
+
}
|
|
23
|
+
/** Which §3.1 action a target status needs — Engineers implement, QA verifies, Product+ skips. */
|
|
24
|
+
export function matrixActionFor(to) {
|
|
25
|
+
if (to === '⏭️')
|
|
26
|
+
return 'matrix_skip';
|
|
27
|
+
if (to === '🔬' || to === '🚀')
|
|
28
|
+
return 'matrix_verify';
|
|
29
|
+
return 'matrix_mark_implemented';
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=matrix-status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"matrix-status.js","sourceRoot":"","sources":["../src/matrix-status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAGnE,0EAA0E;AAC1E,MAAM,CAAC,MAAM,cAAc,GAA4B,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AAEpF,MAAM,UAAU,mBAAmB,CAAC,IAAyB,EAAE,EAAgB;IAC7E,MAAM,CAAC,GAAG,IAAI,IAAI,GAAG,CAAC;IACtB,IAAI,EAAE,KAAK,IAAI;QAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IACnC,MAAM,CAAC,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IACpC,MAAM,CAAC,GAAG,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACrC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,GAAc,EACd,EAAgB,EAChB,OAA4B,EAAE;IAE9B,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC;QACtC,MAAM,IAAI,eAAe,CAAC,eAAe,EAAE,GAAG,CAAC,MAAM,IAAI,GAAG,EAAE,EAAE,CAAC,CAAC;IACpE,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;QAChB,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE;YAAE,MAAM,IAAI,mBAAmB,CAAC,MAAM,CAAC,CAAC;QAChE,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;QACvC,OAAO,EAAE,GAAG,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,KAAK,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;IAClF,CAAC;IACD,OAAO,EAAE,GAAG,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;AAChC,CAAC;AAED,kGAAkG;AAClG,MAAM,UAAU,eAAe,CAC7B,EAAgB;IAEhB,IAAI,EAAE,KAAK,IAAI;QAAE,OAAO,aAAa,CAAC;IACtC,IAAI,EAAE,KAAK,IAAI,IAAI,EAAE,KAAK,IAAI;QAAE,OAAO,eAAe,CAAC;IACvD,OAAO,yBAAyB,CAAC;AACnC,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { type Item, type MatrixRowRecord, type MatrixRowRef, type NewMatrixRow, type QaQueue, type TestType } from './schema.js';
|
|
2
|
+
/**
|
|
3
|
+
* Scaffold `☐` rows for every criterion that has no live row. Any status but `⏭️` is live
|
|
4
|
+
* outright; a `⏭️` row is live for exactly the text it was skipped against — a deliberate
|
|
5
|
+
* "we will not verify this" is a decision about *that* wording, so it keeps covering its
|
|
6
|
+
* criterion while the wording stands and stops the moment the criterion is rewritten (a
|
|
7
|
+
* superseded row is handed over with a text that no longer matches, and so covers nothing).
|
|
8
|
+
* `M` is 1 + the highest ever seen for the `(item, ac)`, superseded rows included — ids are
|
|
9
|
+
* never reused, so a re-scaffold after supersession yields `.M2`.
|
|
10
|
+
*/
|
|
11
|
+
export declare function scaffoldMatrix(items: readonly Item[], existing: readonly MatrixRowRef[]): NewMatrixRow[];
|
|
12
|
+
/**
|
|
13
|
+
* DECISION-018: what `sync_track` does when criteria moved underneath a matrix. A row is superseded
|
|
14
|
+
* when its item has advanced past the version the row quotes **and** the criterion it quotes is
|
|
15
|
+
* gone or now reads differently. Both halves matter: a version bump that did not touch this
|
|
16
|
+
* criterion leaves the row valid, and identical text under a newer version is not drift.
|
|
17
|
+
*
|
|
18
|
+
* A `🚀` row is never superseded — it was verified in production, and that fact does not expire —
|
|
19
|
+
* and a `⏭️` row is already closed. `add` is the scaffold over whatever is left uncovered once the
|
|
20
|
+
* supersessions are applied.
|
|
21
|
+
*/
|
|
22
|
+
export declare function supersedeMatrix(items: readonly Item[], rows: readonly MatrixRowRecord[]): {
|
|
23
|
+
supersede: MatrixRowRecord[];
|
|
24
|
+
add: NewMatrixRow[];
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* §5.6 ship gate: a row that has not reached `🔬` (or been skipped) is unverified work. `strict`
|
|
28
|
+
* refuses the ship, `permissive` reports it and lets it through.
|
|
29
|
+
*
|
|
30
|
+
* An empty matrix is the finding only under `strict`, where there is nothing to have verified.
|
|
31
|
+
* Under `permissive` a track that never ran `matrix_init` passes **silently**: it has no
|
|
32
|
+
* unverified rows, and warning on every such ship would teach callers to ignore `warnings`.
|
|
33
|
+
*/
|
|
34
|
+
export declare function matrixGate(rows: readonly MatrixRowRecord[], enforcement: 'strict' | 'permissive'): {
|
|
35
|
+
ok: boolean;
|
|
36
|
+
blocking: string[];
|
|
37
|
+
warnings: string[];
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* DECISION-020: "what is waiting on QA", grouped `track → environment → rows`. `✅` is implemented
|
|
41
|
+
* and waiting for staging verification, `🔬` is verified on staging and waiting for production.
|
|
42
|
+
* Every other status is somebody else's queue.
|
|
43
|
+
*/
|
|
44
|
+
export declare function qaQueue(rows: readonly MatrixRowRecord[]): QaQueue;
|
|
45
|
+
/** Test types are a closed set and case-sensitive — `'unit'` is a typo, not a synonym. */
|
|
46
|
+
export declare function assertTestType(value: string): TestType;
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"matrix.js","sourceRoot":"","sources":["../src/matrix.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { CriteriaStatus, Role } from './schema.js';
|
|
2
|
+
/** The rows of PRODUCT-SPEC §3.1. `edit_spec` is the context-dependent composite (draft vs reviewed). */
|
|
3
|
+
export declare const ACTIONS: readonly ["read", "create_item", "edit_spec_draft", "review_criteria", "edit_spec_reviewed", "lock_criteria", "unlock_criteria", "schedule_item", "set_priority", "reject_item", "promote_items", "report_progress", "add_follow_up", "suggest_change", "resolve_suggestion", "withdraw_own_suggestion", "matrix_edit_rows", "matrix_mark_implemented", "matrix_verify", "matrix_skip", "matrix_set_enforcement", "ship_track", "add_comment", "manage_org", "manage_own_tokens"];
|
|
4
|
+
export type Action = (typeof ACTIONS)[number] | 'edit_spec';
|
|
5
|
+
type Row = Record<Role, boolean>;
|
|
6
|
+
export declare const PERMISSIONS: Record<(typeof ACTIONS)[number], Row>;
|
|
7
|
+
export declare const MUTATING_ACTIONS: ReadonlySet<Action>;
|
|
8
|
+
export interface PermissionContext {
|
|
9
|
+
criteria_status?: CriteriaStatus;
|
|
10
|
+
token_scope?: 'full' | 'read_only';
|
|
11
|
+
}
|
|
12
|
+
export declare function can(role: Role, action: Action, ctx?: PermissionContext): boolean;
|
|
13
|
+
export declare function assertCan(role: Role, action: Action, ctx?: PermissionContext): void;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { PermissionError } from './errors.js';
|
|
2
|
+
/** The rows of PRODUCT-SPEC §3.1. `edit_spec` is the context-dependent composite (draft vs reviewed). */
|
|
3
|
+
export const ACTIONS = [
|
|
4
|
+
'read',
|
|
5
|
+
'create_item',
|
|
6
|
+
'edit_spec_draft',
|
|
7
|
+
'review_criteria',
|
|
8
|
+
'edit_spec_reviewed',
|
|
9
|
+
'lock_criteria',
|
|
10
|
+
'unlock_criteria',
|
|
11
|
+
'schedule_item',
|
|
12
|
+
'set_priority',
|
|
13
|
+
'reject_item',
|
|
14
|
+
'promote_items',
|
|
15
|
+
'report_progress',
|
|
16
|
+
'add_follow_up',
|
|
17
|
+
'suggest_change',
|
|
18
|
+
'resolve_suggestion',
|
|
19
|
+
'withdraw_own_suggestion',
|
|
20
|
+
'matrix_edit_rows',
|
|
21
|
+
'matrix_mark_implemented',
|
|
22
|
+
'matrix_verify',
|
|
23
|
+
'matrix_skip',
|
|
24
|
+
'matrix_set_enforcement',
|
|
25
|
+
'ship_track',
|
|
26
|
+
'add_comment',
|
|
27
|
+
'manage_org',
|
|
28
|
+
'manage_own_tokens',
|
|
29
|
+
];
|
|
30
|
+
const r = (owner, product, engineer, qa, viewer) => ({ owner, product, engineer, qa, viewer });
|
|
31
|
+
export const PERMISSIONS = {
|
|
32
|
+
read: r(true, true, true, true, true),
|
|
33
|
+
create_item: r(true, true, true, true, false),
|
|
34
|
+
edit_spec_draft: r(true, true, true, true, false),
|
|
35
|
+
review_criteria: r(true, true, true, true, false),
|
|
36
|
+
edit_spec_reviewed: r(true, true, false, false, false),
|
|
37
|
+
lock_criteria: r(true, true, false, false, false),
|
|
38
|
+
unlock_criteria: r(true, true, false, false, false),
|
|
39
|
+
schedule_item: r(true, true, false, false, false),
|
|
40
|
+
set_priority: r(true, true, false, false, false),
|
|
41
|
+
reject_item: r(true, true, false, false, false),
|
|
42
|
+
promote_items: r(true, true, true, false, false),
|
|
43
|
+
report_progress: r(true, true, true, false, false),
|
|
44
|
+
add_follow_up: r(true, true, true, true, false),
|
|
45
|
+
suggest_change: r(true, true, true, true, false),
|
|
46
|
+
// Author scoping ("own") is an identity check the matrix has no vocabulary for; `review.ts`
|
|
47
|
+
// adds it on top of this row (docs/service/review-layer.md).
|
|
48
|
+
resolve_suggestion: r(true, true, false, false, false),
|
|
49
|
+
withdraw_own_suggestion: r(true, true, true, true, false),
|
|
50
|
+
matrix_edit_rows: r(true, true, true, true, false),
|
|
51
|
+
matrix_mark_implemented: r(true, true, true, false, false),
|
|
52
|
+
matrix_verify: r(true, true, false, true, false),
|
|
53
|
+
matrix_skip: r(true, true, false, false, false),
|
|
54
|
+
matrix_set_enforcement: r(true, true, false, false, false),
|
|
55
|
+
ship_track: r(true, true, false, false, false),
|
|
56
|
+
add_comment: r(true, true, true, true, false),
|
|
57
|
+
manage_org: r(true, false, false, false, false),
|
|
58
|
+
manage_own_tokens: r(true, true, true, true, true),
|
|
59
|
+
};
|
|
60
|
+
// `manage_own_tokens` is deliberately in here: a `read_only` token must not mint or revoke tokens.
|
|
61
|
+
export const MUTATING_ACTIONS = new Set([
|
|
62
|
+
...ACTIONS.filter((a) => a !== 'read'),
|
|
63
|
+
'edit_spec',
|
|
64
|
+
]);
|
|
65
|
+
export function can(role, action, ctx = {}) {
|
|
66
|
+
if (ctx.token_scope === 'read_only' && MUTATING_ACTIONS.has(action))
|
|
67
|
+
return false;
|
|
68
|
+
if (action === 'edit_spec') {
|
|
69
|
+
switch (ctx.criteria_status) {
|
|
70
|
+
case 'draft':
|
|
71
|
+
return PERMISSIONS.edit_spec_draft[role];
|
|
72
|
+
case 'reviewed':
|
|
73
|
+
return PERMISSIONS.edit_spec_reviewed[role];
|
|
74
|
+
case 'locked':
|
|
75
|
+
return false; // §5.4 rule 1 — nobody edits a locked spec; unlock first
|
|
76
|
+
default:
|
|
77
|
+
return false; // caller must say which tier the item is in
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return PERMISSIONS[action][role];
|
|
81
|
+
}
|
|
82
|
+
export function assertCan(role, action, ctx = {}) {
|
|
83
|
+
if (!can(role, action, ctx)) {
|
|
84
|
+
throw new PermissionError(role, action, ctx.criteria_status ? `criteria_status: ${ctx.criteria_status}` : undefined);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=permissions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permissions.js","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Criterion, Item, Track } from './schema.js';
|
|
2
|
+
export interface PromotePayload {
|
|
3
|
+
track: Track;
|
|
4
|
+
items: Item[];
|
|
5
|
+
spec: Array<{
|
|
6
|
+
id: string;
|
|
7
|
+
title: string;
|
|
8
|
+
criteria: Criterion[];
|
|
9
|
+
out_of_scope: string;
|
|
10
|
+
criteria_version: number;
|
|
11
|
+
}>;
|
|
12
|
+
}
|
|
13
|
+
/** §5.4 rule 5 + §6.2 promote_items: refuses unless every item is locked; no --force exists. */
|
|
14
|
+
export declare function promoteItems(items: Item[], opts: {
|
|
15
|
+
slug: string;
|
|
16
|
+
type: string;
|
|
17
|
+
}): PromotePayload;
|
|
18
|
+
/** Ported from liblib-meet scripts/backlog/lib/items.ts — same output for the same input. */
|
|
19
|
+
export declare function slugify(title: string): string;
|
|
20
|
+
export declare function buildTrackSlug(primaryTitle: string, date: string): string;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { NotLockedError } from './errors.js';
|
|
2
|
+
import { transitionStatus } from './status.js';
|
|
3
|
+
/** §5.4 rule 5 + §6.2 promote_items: refuses unless every item is locked; no --force exists. */
|
|
4
|
+
export function promoteItems(items, opts) {
|
|
5
|
+
for (const it of items)
|
|
6
|
+
if (it.criteria_status !== 'locked')
|
|
7
|
+
throw new NotLockedError(it.id, it.criteria_status);
|
|
8
|
+
const promoted = items.map((it) => {
|
|
9
|
+
const staged = it.status === 'backlog' ? transitionStatus(it, 'scheduled') : it;
|
|
10
|
+
return { ...transitionStatus(staged, 'promoted'), track: opts.slug };
|
|
11
|
+
});
|
|
12
|
+
const track = {
|
|
13
|
+
slug: opts.slug,
|
|
14
|
+
type: opts.type,
|
|
15
|
+
backlog_ids: promoted.map((i) => i.id),
|
|
16
|
+
follow_up_backlog: [],
|
|
17
|
+
qa_doc: null,
|
|
18
|
+
qa_enforcement: 'permissive',
|
|
19
|
+
spec_drift: false,
|
|
20
|
+
shipment: null,
|
|
21
|
+
};
|
|
22
|
+
return {
|
|
23
|
+
track,
|
|
24
|
+
items: promoted,
|
|
25
|
+
spec: promoted.map((i) => ({
|
|
26
|
+
id: i.id,
|
|
27
|
+
title: i.title,
|
|
28
|
+
criteria: i.criteria,
|
|
29
|
+
out_of_scope: i.out_of_scope,
|
|
30
|
+
criteria_version: i.criteria_version,
|
|
31
|
+
})),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/** Ported from liblib-meet scripts/backlog/lib/items.ts — same output for the same input. */
|
|
35
|
+
export function slugify(title) {
|
|
36
|
+
return title
|
|
37
|
+
.toLowerCase()
|
|
38
|
+
.normalize('NFKD')
|
|
39
|
+
.replace(/[̀-ͯ]/g, '')
|
|
40
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
41
|
+
.replace(/^-+|-+$/g, '')
|
|
42
|
+
.slice(0, 60);
|
|
43
|
+
}
|
|
44
|
+
export function buildTrackSlug(primaryTitle, date) {
|
|
45
|
+
return `${slugify(primaryTitle)}_${date.replace(/-/g, '')}`;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=promote.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promote.js","sourceRoot":"","sources":["../src/promote.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAc/C,gGAAgG;AAChG,MAAM,UAAU,YAAY,CAAC,KAAa,EAAE,IAAoC;IAC9E,KAAK,MAAM,EAAE,IAAI,KAAK;QACpB,IAAI,EAAE,CAAC,eAAe,KAAK,QAAQ;YAAE,MAAM,IAAI,cAAc,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,eAAe,CAAC,CAAC;IAC3F,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE;QAChC,MAAM,MAAM,GAAG,EAAE,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,gBAAgB,CAAC,EAAE,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAChF,OAAO,EAAE,GAAG,gBAAgB,CAAC,MAAM,EAAE,UAAU,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;IACvE,CAAC,CAAC,CAAC;IACH,MAAM,KAAK,GAAU;QACnB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,WAAW,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACtC,iBAAiB,EAAE,EAAE;QACrB,MAAM,EAAE,IAAI;QACZ,cAAc,EAAE,YAAY;QAC5B,UAAU,EAAE,KAAK;QACjB,QAAQ,EAAE,IAAI;KACf,CAAC;IACF,OAAO;QACL,KAAK;QACL,KAAK,EAAE,QAAQ;QACf,IAAI,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACzB,EAAE,EAAE,CAAC,CAAC,EAAE;YACR,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,YAAY,EAAE,CAAC,CAAC,YAAY;YAC5B,gBAAgB,EAAE,CAAC,CAAC,gBAAgB;SACrC,CAAC,CAAC;KACJ,CAAC;AACJ,CAAC;AAED,6FAA6F;AAC7F,MAAM,UAAU,OAAO,CAAC,KAAa;IACnC,OAAO,KAAK;SACT,WAAW,EAAE;SACb,SAAS,CAAC,MAAM,CAAC;SACjB,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC;SACrB,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,YAAoB,EAAE,IAAY;IAC/D,OAAO,GAAG,OAAO,CAAC,YAAY,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC;AAC9D,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Item } from './schema.js';
|
|
2
|
+
/** §6.2 `next_ready`: locked, not yet promoted, and every prerequisite already shipped. */
|
|
3
|
+
export declare function isReady(item: Item, shippedRefs: ReadonlySet<string>): boolean;
|
|
4
|
+
/**
|
|
5
|
+
* The "what should I work on" answer: ready items ranked by priority, then age, then id.
|
|
6
|
+
* Pure — the input array is never reordered in place.
|
|
7
|
+
*/
|
|
8
|
+
export declare function nextReady(items: readonly Item[], shippedRefs: ReadonlySet<string>): Item[];
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** `P0` first. Sorting on the string would put `P0` first too, but only by accident. */
|
|
2
|
+
const PRIORITY_RANK = { P0: 0, P1: 1, P2: 2, P3: 3, P4: 4 };
|
|
3
|
+
/** §6.2 `next_ready`: locked, not yet promoted, and every prerequisite already shipped. */
|
|
4
|
+
export function isReady(item, shippedRefs) {
|
|
5
|
+
if (item.criteria_status !== 'locked')
|
|
6
|
+
return false;
|
|
7
|
+
if (item.status !== 'backlog' && item.status !== 'scheduled')
|
|
8
|
+
return false;
|
|
9
|
+
return item.prerequisites.every((ref) => shippedRefs.has(ref));
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* The "what should I work on" answer: ready items ranked by priority, then age, then id.
|
|
13
|
+
* Pure — the input array is never reordered in place.
|
|
14
|
+
*/
|
|
15
|
+
export function nextReady(items, shippedRefs) {
|
|
16
|
+
return items
|
|
17
|
+
.filter((it) => isReady(it, shippedRefs))
|
|
18
|
+
.sort((a, b) => PRIORITY_RANK[a.priority] - PRIORITY_RANK[b.priority] ||
|
|
19
|
+
a.created.localeCompare(b.created) ||
|
|
20
|
+
a.id.localeCompare(b.id));
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=ready.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ready.js","sourceRoot":"","sources":["../src/ready.ts"],"names":[],"mappings":"AAEA,wFAAwF;AACxF,MAAM,aAAa,GAA6B,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;AAEtF,2FAA2F;AAC3F,MAAM,UAAU,OAAO,CAAC,IAAU,EAAE,WAAgC;IAClE,IAAI,IAAI,CAAC,eAAe,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACpD,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,KAAK,WAAW;QAAE,OAAO,KAAK,CAAC;IAC3E,OAAO,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;AACjE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,KAAsB,EAAE,WAAgC;IAChF,OAAO,KAAK;SACT,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,EAAE,WAAW,CAAC,CAAC;SACxC,IAAI,CACH,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACP,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC;QACrD,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC;QAClC,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAC3B,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { type SpecField } from './criteria.js';
|
|
2
|
+
import type { CommentTarget, Item, Revision, Role, Suggestion, SuggestionStatus } from './schema.js';
|
|
3
|
+
export interface Actor {
|
|
4
|
+
id: string;
|
|
5
|
+
role: Role;
|
|
6
|
+
}
|
|
7
|
+
/** One-way, in the house style of `status.ts`: nothing leaves a resolved suggestion. */
|
|
8
|
+
export declare const SUGGESTION_TRANSITIONS: ReadonlyArray<readonly [SuggestionStatus, SuggestionStatus]>;
|
|
9
|
+
export declare function canTransitionSuggestion(from: SuggestionStatus, to: SuggestionStatus): boolean;
|
|
10
|
+
export interface FileSuggestionInput {
|
|
11
|
+
id: string;
|
|
12
|
+
text: string;
|
|
13
|
+
rationale: string;
|
|
14
|
+
target?: CommentTarget;
|
|
15
|
+
created_at: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* A suggestion is only meaningful on a **locked** item: on `draft` Engineer and QA can just edit,
|
|
19
|
+
* on `reviewed` they comment. Filing against anything else is refused with the current status.
|
|
20
|
+
*/
|
|
21
|
+
export declare function fileSuggestion(item: Item, input: FileSuggestionInput, actor: Actor): Suggestion;
|
|
22
|
+
export type SuggestionResolution = {
|
|
23
|
+
accept: true;
|
|
24
|
+
patch: Partial<Pick<Item, SpecField>>;
|
|
25
|
+
note?: string;
|
|
26
|
+
} | {
|
|
27
|
+
accept: false;
|
|
28
|
+
note: string;
|
|
29
|
+
};
|
|
30
|
+
export interface ResolveSuggestionResult {
|
|
31
|
+
item: Item;
|
|
32
|
+
suggestion: Suggestion;
|
|
33
|
+
revision: Revision | null;
|
|
34
|
+
/** The caller sets `spec_drift` on the track; core stays pure, as `unlockCriteria` already does. */
|
|
35
|
+
flagDrift: boolean;
|
|
36
|
+
}
|
|
37
|
+
/** §5.4 rule 4 — accept (unlock → edit → re-lock as one action) or decline with a note. */
|
|
38
|
+
export declare function resolveSuggestion(item: Item, suggestion: Suggestion, resolution: SuggestionResolution, actor: Actor, opts: {
|
|
39
|
+
now: string;
|
|
40
|
+
}): ResolveSuggestionResult;
|
|
41
|
+
/** Mechanical, not a judgement call: the item's version moved past the one this was filed against. */
|
|
42
|
+
export declare function supersede(suggestion: Suggestion, now: string): Suggestion;
|
|
43
|
+
/**
|
|
44
|
+
* Author-scoped: the matrix row says *may withdraw a suggestion*, the identity check says *own*.
|
|
45
|
+
* The Owner is excepted — someone has to be able to close a departed author's suggestion.
|
|
46
|
+
*/
|
|
47
|
+
export declare function withdrawSuggestion(suggestion: Suggestion, actor: Actor, opts: {
|
|
48
|
+
now: string;
|
|
49
|
+
}): Suggestion;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { SPEC_FIELDS } from './criteria.js';
|
|
2
|
+
import { NotLockedError, PermissionError, ReasonRequiredError, SupersededError, TransitionError, } from './errors.js';
|
|
3
|
+
import { assertCan } from './permissions.js';
|
|
4
|
+
import { specDiffFields } from './spec-diff.js';
|
|
5
|
+
/** One-way, in the house style of `status.ts`: nothing leaves a resolved suggestion. */
|
|
6
|
+
export const SUGGESTION_TRANSITIONS = [
|
|
7
|
+
['open', 'accepted'],
|
|
8
|
+
['open', 'declined'],
|
|
9
|
+
['open', 'withdrawn'],
|
|
10
|
+
['open', 'superseded'],
|
|
11
|
+
];
|
|
12
|
+
export function canTransitionSuggestion(from, to) {
|
|
13
|
+
return SUGGESTION_TRANSITIONS.some(([f, t]) => f === from && t === to);
|
|
14
|
+
}
|
|
15
|
+
function assertOpen(suggestion, to) {
|
|
16
|
+
if (!canTransitionSuggestion(suggestion.status, to))
|
|
17
|
+
throw new TransitionError('suggestion_status', suggestion.status, to);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* A suggestion is only meaningful on a **locked** item: on `draft` Engineer and QA can just edit,
|
|
21
|
+
* on `reviewed` they comment. Filing against anything else is refused with the current status.
|
|
22
|
+
*/
|
|
23
|
+
export function fileSuggestion(item, input, actor) {
|
|
24
|
+
assertCan(actor.role, 'suggest_change');
|
|
25
|
+
if (item.criteria_status !== 'locked')
|
|
26
|
+
throw new NotLockedError(item.id, item.criteria_status);
|
|
27
|
+
if (!input.text.trim())
|
|
28
|
+
throw new ReasonRequiredError('suggest_change (text)');
|
|
29
|
+
if (!input.rationale.trim())
|
|
30
|
+
throw new ReasonRequiredError('suggest_change (rationale)');
|
|
31
|
+
return {
|
|
32
|
+
id: input.id,
|
|
33
|
+
item_id: item.id,
|
|
34
|
+
criteria_version: item.criteria_version,
|
|
35
|
+
target: input.target ?? { kind: 'item' },
|
|
36
|
+
text: input.text,
|
|
37
|
+
rationale: input.rationale,
|
|
38
|
+
status: 'open',
|
|
39
|
+
created_by: actor.id,
|
|
40
|
+
created_at: input.created_at,
|
|
41
|
+
resolved_by: null,
|
|
42
|
+
resolved_at: null,
|
|
43
|
+
resolution_note: null,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
const SPEC_FIELD_SET = new Set(SPEC_FIELDS);
|
|
47
|
+
/**
|
|
48
|
+
* The locked-bypassing edit. `editSpecFields` refuses a locked item and must keep refusing it —
|
|
49
|
+
* an accepted suggestion is a *controlled amendment*, not an unlock: Product has already read the
|
|
50
|
+
* exact proposed text and ruled on it, so the item comes out `locked` with a higher version.
|
|
51
|
+
*/
|
|
52
|
+
function applySpecPatch(item, patch) {
|
|
53
|
+
for (const key of Object.keys(patch)) {
|
|
54
|
+
if (!SPEC_FIELD_SET.has(key))
|
|
55
|
+
throw new Error(`${item.id}: ${key} is not a spec field; a suggestion may only change ${SPEC_FIELDS.join(', ')}`);
|
|
56
|
+
}
|
|
57
|
+
return { ...item, ...patch, criteria_version: item.criteria_version + 1 };
|
|
58
|
+
}
|
|
59
|
+
/** §5.4 rule 4 — accept (unlock → edit → re-lock as one action) or decline with a note. */
|
|
60
|
+
export function resolveSuggestion(item, suggestion, resolution, actor, opts) {
|
|
61
|
+
assertCan(actor.role, 'resolve_suggestion');
|
|
62
|
+
const { now } = opts;
|
|
63
|
+
const to = resolution.accept ? 'accepted' : 'declined';
|
|
64
|
+
if (suggestion.item_id !== item.id)
|
|
65
|
+
throw new Error(`suggestion ${suggestion.id} was filed against ${suggestion.item_id}, not ${item.id}`);
|
|
66
|
+
assertOpen(suggestion, to);
|
|
67
|
+
if (suggestion.criteria_version !== item.criteria_version) {
|
|
68
|
+
// The closure travels with the refusal: the caller persists `err.suggestion` and rethrows.
|
|
69
|
+
throw new SupersededError(supersede(suggestion, now), suggestion.criteria_version, item.criteria_version);
|
|
70
|
+
}
|
|
71
|
+
if (item.criteria_status !== 'locked')
|
|
72
|
+
throw new NotLockedError(item.id, item.criteria_status);
|
|
73
|
+
const note = resolution.note?.trim() ?? '';
|
|
74
|
+
if (!resolution.accept) {
|
|
75
|
+
if (!note)
|
|
76
|
+
throw new ReasonRequiredError('decline a suggestion');
|
|
77
|
+
return {
|
|
78
|
+
item,
|
|
79
|
+
suggestion: {
|
|
80
|
+
...suggestion,
|
|
81
|
+
status: 'declined',
|
|
82
|
+
resolved_by: actor.id,
|
|
83
|
+
resolved_at: now,
|
|
84
|
+
resolution_note: note,
|
|
85
|
+
},
|
|
86
|
+
revision: null,
|
|
87
|
+
flagDrift: false,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
const next = applySpecPatch(item, resolution.patch);
|
|
91
|
+
const revision = {
|
|
92
|
+
id: null,
|
|
93
|
+
item_id: item.id,
|
|
94
|
+
version_before: item.criteria_version,
|
|
95
|
+
version_after: next.criteria_version,
|
|
96
|
+
fields: specDiffFields(item, next),
|
|
97
|
+
source: 'suggestion_accept',
|
|
98
|
+
reason: note ? `${suggestion.rationale} — ${note}` : suggestion.rationale,
|
|
99
|
+
changed_by: actor.id,
|
|
100
|
+
changed_at: now,
|
|
101
|
+
};
|
|
102
|
+
return {
|
|
103
|
+
item: next,
|
|
104
|
+
suggestion: {
|
|
105
|
+
...suggestion,
|
|
106
|
+
status: 'accepted',
|
|
107
|
+
resolved_by: actor.id,
|
|
108
|
+
resolved_at: now,
|
|
109
|
+
resolution_note: note || null,
|
|
110
|
+
},
|
|
111
|
+
revision,
|
|
112
|
+
// Same rule as `unlockCriteria`: the version moved under a track that is already building.
|
|
113
|
+
flagDrift: next.status === 'promoted' || next.status === 'in-progress',
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
/** Mechanical, not a judgement call: the item's version moved past the one this was filed against. */
|
|
117
|
+
export function supersede(suggestion, now) {
|
|
118
|
+
assertOpen(suggestion, 'superseded');
|
|
119
|
+
return { ...suggestion, status: 'superseded', resolved_at: now, resolution_note: null };
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Author-scoped: the matrix row says *may withdraw a suggestion*, the identity check says *own*.
|
|
123
|
+
* The Owner is excepted — someone has to be able to close a departed author's suggestion.
|
|
124
|
+
*/
|
|
125
|
+
export function withdrawSuggestion(suggestion, actor, opts) {
|
|
126
|
+
assertCan(actor.role, 'withdraw_own_suggestion');
|
|
127
|
+
if (suggestion.created_by !== actor.id && actor.role !== 'owner')
|
|
128
|
+
throw new PermissionError(actor.role, 'withdraw_own_suggestion', 'not the author');
|
|
129
|
+
assertOpen(suggestion, 'withdrawn');
|
|
130
|
+
return {
|
|
131
|
+
...suggestion,
|
|
132
|
+
status: 'withdrawn',
|
|
133
|
+
resolved_by: actor.id,
|
|
134
|
+
resolved_at: opts.now,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=review.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review.js","sourceRoot":"","sources":["../src/review.ts"],"names":[],"mappings":"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"}
|