@difflab/difflab-cli 0.8.1 → 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/README.md +9 -1
- package/dist/index.js +387 -107
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -66,12 +66,20 @@ difflab-cli templates list
|
|
|
66
66
|
difflab-cli templates scaffold spec-driven-plan docs/plans first-plan.md --cwd /absolute/project/path
|
|
67
67
|
```
|
|
68
68
|
|
|
69
|
-
The CLI defaults `--cwd` to its current directory. The MCP server exposes `template_list({})` and `scaffold({"name":"spec-driven-plan","cwd":"/absolute/project/path","path":"docs/plans","filename":"first-plan.md"})`. Both interfaces take a hyphenated template name, a relative destination **directory** (`path`, or `.` for `cwd`), and a separate **filename**. MCP always requires an absolute existing `cwd`. Missing destination directories are created. Available names: `spec-driven-plan`, `software-architecture-design`, `architecture-decision-record`, `product-requirements-document`, `code-review`, `planning-intent`, `ui-component-architecture`, `pull-request-description` (adapted from Diffpi's PR template), `flow-definition`, `flow-instance`, `exploration-summary`, `exploration-research`, `exploration-proposal`, and `poc-readme`.
|
|
69
|
+
The CLI defaults `--cwd` to its current directory. The MCP server exposes `template_list({})` and `scaffold({"name":"spec-driven-plan","cwd":"/absolute/project/path","path":"docs/plans","filename":"first-plan.md"})`. Both interfaces take a hyphenated template name, a relative destination **directory** (`path`, or `.` for `cwd`), and a separate **filename**. MCP always requires an absolute existing `cwd`. Missing destination directories are created. Available names: `spec-driven-plan`, `software-architecture-design`, `architecture-decision-record`, `product-requirements-document`, `agents`, `code-review`, `planning-intent`, `ui-component-architecture`, `pull-request-description` (adapted from Diffpi's PR template), `flow-definition`, `flow-instance`, `exploration-summary`, `exploration-research`, `exploration-proposal`, and `poc-readme`.
|
|
70
|
+
|
|
71
|
+
Use `difflab-cli templates scaffold agents . AGENTS.md --cwd /absolute/project/path` to start repository guidance. Fill the prompts with known rules and remove unused prompts. The template separates Hard Rules, Organization, Style, and Testing. Hard Rules name required task runners and repository tools. Organization separates directory layout from section headers, code ordering, and grouping within files. Review addressing saves explicit user repository rules immediately and marks inferred or uncertain rules with `[Proposed]`. It asks before reorganizing existing guidance to match the template and adapts the existing structure if permission is declined. It retains terse file-level organization rules and keeps guidance within 500 soft-wrapped source lines.
|
|
70
72
|
|
|
71
73
|
On first use, missing bundled templates and filled examples are copied to `~/.difflab/templates` and `~/.difflab/templates/examples`. Edit the installed templates to customize future documents. Later runs add missing files but never replace existing ones, even after an upgrade. Examples are for reference, not selectable templates. Copying is literal: prompts and placeholders are not substituted. An unknown name, missing or invalid source, unsafe path, symlink, or existing destination causes an error; destination files are never overwritten. The target directory must remain inside `cwd` and the filename must be a single file name without separators. The one symlink exception is an initialized repository's `.difflab` link to its own user project store: `templates scaffold spec-driven-plan .difflab/plans/YYMMDD-name PLAN.md --cwd /absolute/repo` works in Git worktrees as well. To restore a bundled default, move your installed copy aside before listing again.
|
|
72
74
|
|
|
73
75
|
The portable `skills/difflab-plan`, `skills/difflab-review`, `skills/difflab-flow`, `skills/difflab-explore`, `skills/difflab-adr`, and `skills/difflab-poc` directories contain natural-language agent workflows for these templates. Install each skill in your client's Agent Skills directory. The MCP-only `log_append({"path":"/absolute/physical/project-store/plans/YYMMDD-name/logs.txt","message":"Phase started"})` tool creates an append-only progress log on first use; pass the physical path, not the `.difflab` symlink. There is no log CLI command.
|
|
74
76
|
|
|
77
|
+
The [plan authoring guide](../../skills/difflab-plan/references/authoring.md) requires component/process/artifact visuals, compact interfaces, and a nested repo tree at the start of `Consequences`. Add a `Testing` outline of exact end-to-end and integration scenarios. Keep expected commit and green-CI gates, including for `plan go --commit`. That flag permits scoped commits. Push requires separate authorization. Write `N/A.` when infrastructure stays unchanged. Source examples live in the skill's `references/examples/` and are not CLI templates.
|
|
78
|
+
|
|
79
|
+
The [review rubric](../../skills/difflab-review/references/rubric.md) follows language conventions, codebase patterns, and applicable `AGENTS.md` rules. Refactor unclear code at the scope needed. Review application behavior first, then module public APIs, then focused unit behavior. Require clear Arrange/Act/Assert blocks and useful fixtures. Untested private functions alone are not findings. Keep comments short and use simple English. Omit nitpicks and minor optimizations. Explicit security/performance focuses deepen those audits.
|
|
80
|
+
|
|
81
|
+
For an upgrade, reinstall changed skills and reload the client. Back up and move aside only the installed templates/examples you want refreshed, then list templates again. For these changes, inspect `~/.difflab/templates/{spec-driven-plan,code-review}.md` and `examples/{plan-feature,plan-migration,review-change}.md`. Preserve customizations in backups and merge deliberately; never reset the whole directory. Older templates still work: skills add missing subsections to generated documents while preserving human content and existing phase completion state.
|
|
82
|
+
|
|
75
83
|
## Flow definitions and runs
|
|
76
84
|
|
|
77
85
|
`flow new <name>` and `flow go <name>` are Agent Skill requests, not CLI subcommands. The agent creates a definition once at `~/.difflab/flows/<name>.md`. It calls MCP `scaffold` with `cwd=<absolute home>/.difflab`, `path="flows"`, and `filename="<name>.md"`. Do not use `cwd=<home>,path=".difflab/flows"`: that route is for a repository project store. You can edit a definition before another run. A new run uses a fresh snapshot and does not alter old runs.
|
package/dist/index.js
CHANGED
|
@@ -37320,6 +37320,37 @@ var require_utils = __commonJS(function(exports, module) {
|
|
|
37320
37320
|
}
|
|
37321
37321
|
return output.join("");
|
|
37322
37322
|
}
|
|
37323
|
+
function removeDotSegmentsRelative(path) {
|
|
37324
|
+
if (path === "" || path[0] === "/") {
|
|
37325
|
+
return removeDotSegments(path);
|
|
37326
|
+
}
|
|
37327
|
+
const segments = path.split("/");
|
|
37328
|
+
const last = segments.length - 1;
|
|
37329
|
+
const output = [];
|
|
37330
|
+
let trailingDirectory = false;
|
|
37331
|
+
for (let i = 0;i <= last; i++) {
|
|
37332
|
+
const segment = segments[i];
|
|
37333
|
+
if (segment === "." || segment === "..") {
|
|
37334
|
+
if (segment === "..") {
|
|
37335
|
+
if (output.length !== 0 && output[output.length - 1] !== "..") {
|
|
37336
|
+
output.pop();
|
|
37337
|
+
} else {
|
|
37338
|
+
output.push("..");
|
|
37339
|
+
}
|
|
37340
|
+
}
|
|
37341
|
+
trailingDirectory = i === last;
|
|
37342
|
+
} else {
|
|
37343
|
+
output.push(segment);
|
|
37344
|
+
}
|
|
37345
|
+
}
|
|
37346
|
+
if (trailingDirectory && output[output.length - 1] !== "..") {
|
|
37347
|
+
output.push("");
|
|
37348
|
+
}
|
|
37349
|
+
if (output.length === 0 || output[0] === "" || output[0].includes(":")) {
|
|
37350
|
+
output.unshift(".");
|
|
37351
|
+
}
|
|
37352
|
+
return output.join("/");
|
|
37353
|
+
}
|
|
37323
37354
|
var HOST_DELIMS = { "@": "%40", "/": "%2F", "?": "%3F", "#": "%23", ":": "%3A" };
|
|
37324
37355
|
var HOST_DELIM_RE = /[@/?#:]/g;
|
|
37325
37356
|
var HOST_DELIM_NO_COLON_RE = /[@/?#]/g;
|
|
@@ -37580,6 +37611,7 @@ var require_utils = __commonJS(function(exports, module) {
|
|
|
37580
37611
|
encodeFragment,
|
|
37581
37612
|
escapePreservingEscapes,
|
|
37582
37613
|
removeDotSegments,
|
|
37614
|
+
removeDotSegmentsRelative,
|
|
37583
37615
|
isIPv4,
|
|
37584
37616
|
isUUID,
|
|
37585
37617
|
normalizeIPv6,
|
|
@@ -37764,16 +37796,18 @@ var require_schemes = __commonJS(function(exports, module) {
|
|
|
37764
37796
|
|
|
37765
37797
|
// ../../node_modules/fast-uri/index.js
|
|
37766
37798
|
var require_fast_uri = __commonJS(function(exports, module) {
|
|
37767
|
-
var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, serializePathEncoding, normalizeQueryFragmentEncoding, encodeQuery, encodeFragment, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
|
|
37799
|
+
var { normalizeIPv6, removeDotSegments, removeDotSegmentsRelative, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, serializePathEncoding, normalizeQueryFragmentEncoding, encodeQuery, encodeFragment, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
|
|
37768
37800
|
var { SCHEMES, getSchemeHandler } = require_schemes();
|
|
37769
37801
|
var VALID_SCHEME = /^[A-Za-z][A-Za-z0-9+.-]*$/u;
|
|
37770
37802
|
var MALFORMED_SCHEME_ERROR = "URI scheme is malformed.";
|
|
37771
|
-
|
|
37772
|
-
|
|
37773
|
-
|
|
37803
|
+
var MALFORMED_HOST_ERROR = "URI host is malformed.";
|
|
37804
|
+
var FORBIDDEN_DOMAIN_HOST_CHARACTER = /[\0-\x20"#/:<>?@[\\\]^`{|}\x7F]/u;
|
|
37805
|
+
function validateScheme(scheme) {
|
|
37806
|
+
const stringScheme = String(scheme);
|
|
37807
|
+
if (!VALID_SCHEME.test(stringScheme)) {
|
|
37774
37808
|
throw new TypeError(MALFORMED_SCHEME_ERROR);
|
|
37775
37809
|
}
|
|
37776
|
-
return
|
|
37810
|
+
return stringScheme;
|
|
37777
37811
|
}
|
|
37778
37812
|
function normalize(uri, options) {
|
|
37779
37813
|
if (typeof uri === "string") {
|
|
@@ -37808,6 +37842,9 @@ var require_fast_uri = __commonJS(function(exports, module) {
|
|
|
37808
37842
|
const resolvedSchemeHandler = getSchemeHandler(options && options.scheme || resolved.scheme);
|
|
37809
37843
|
const resolvedHost = resolved.host;
|
|
37810
37844
|
const resolvedHostIsIP = resolvedHost !== undefined && resolvedHost !== "" && (isIPv4(resolvedHost) || normalizeIPv6(resolvedHost).isIPV6);
|
|
37845
|
+
if (hasForbiddenDomainHost(resolved, options || {}, resolvedSchemeHandler, resolvedHostIsIP)) {
|
|
37846
|
+
throw new Error(MALFORMED_HOST_ERROR);
|
|
37847
|
+
}
|
|
37811
37848
|
canonicalizeHost(resolved, options || {}, resolvedSchemeHandler, resolvedHostIsIP);
|
|
37812
37849
|
const encodedASCIIHost = resolvedHost && resolvedHost.indexOf("%") !== -1 && !/\P{ASCII}/u.test(resolvedHost);
|
|
37813
37850
|
if (resolved.error && !encodedASCIIHost) {
|
|
@@ -37849,14 +37886,15 @@ var require_fast_uri = __commonJS(function(exports, module) {
|
|
|
37849
37886
|
if (relative.path[0] === "/") {
|
|
37850
37887
|
target.path = removeDotSegments(relative.path);
|
|
37851
37888
|
} else {
|
|
37852
|
-
|
|
37889
|
+
const baseHasAuthority = base.userinfo !== undefined || base.host !== undefined || base.port !== undefined;
|
|
37890
|
+
if (baseHasAuthority && !base.path) {
|
|
37853
37891
|
target.path = "/" + relative.path;
|
|
37854
37892
|
} else if (!base.path) {
|
|
37855
37893
|
target.path = relative.path;
|
|
37856
37894
|
} else {
|
|
37857
37895
|
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative.path;
|
|
37858
37896
|
}
|
|
37859
|
-
target.path = removeDotSegments(target.path);
|
|
37897
|
+
target.path = base.scheme === undefined && !baseHasAuthority && target.path[0] !== "/" ? removeDotSegmentsRelative(target.path) : removeDotSegments(target.path);
|
|
37860
37898
|
}
|
|
37861
37899
|
target.query = relative.query;
|
|
37862
37900
|
}
|
|
@@ -37894,13 +37932,14 @@ var require_fast_uri = __commonJS(function(exports, module) {
|
|
|
37894
37932
|
const options = Object.assign({}, opts);
|
|
37895
37933
|
const uriTokens = [];
|
|
37896
37934
|
if (component.scheme) {
|
|
37897
|
-
component.scheme =
|
|
37935
|
+
component.scheme = validateScheme(component.scheme);
|
|
37898
37936
|
}
|
|
37899
37937
|
const schemeHandler = getSchemeHandler(options.scheme || component.scheme);
|
|
37900
37938
|
if (schemeHandler && schemeHandler.serialize)
|
|
37901
37939
|
schemeHandler.serialize(component, options);
|
|
37902
37940
|
const hasAuthority = component.userinfo !== undefined || component.host !== undefined || component.port !== undefined;
|
|
37903
|
-
const
|
|
37941
|
+
const schemelessReference = (component.scheme === undefined || options.reference === "suffix") && !hasAuthority;
|
|
37942
|
+
const pathNoScheme = !options.skipEscape && schemelessReference;
|
|
37904
37943
|
if (component.path !== undefined) {
|
|
37905
37944
|
if (!options.skipEscape) {
|
|
37906
37945
|
component.path = serializePathEncoding(component.path, pathNoScheme);
|
|
@@ -37909,7 +37948,7 @@ var require_fast_uri = __commonJS(function(exports, module) {
|
|
|
37909
37948
|
}
|
|
37910
37949
|
}
|
|
37911
37950
|
if (options.reference !== "suffix" && component.scheme) {
|
|
37912
|
-
component.scheme =
|
|
37951
|
+
component.scheme = validateScheme(component.scheme);
|
|
37913
37952
|
uriTokens.push(component.scheme, ":");
|
|
37914
37953
|
}
|
|
37915
37954
|
const authority = recomposeAuthority(component);
|
|
@@ -37925,9 +37964,9 @@ var require_fast_uri = __commonJS(function(exports, module) {
|
|
|
37925
37964
|
if (component.path !== undefined) {
|
|
37926
37965
|
let s = component.path;
|
|
37927
37966
|
if (!options.absolutePath && (!schemeHandler || !schemeHandler.absolutePath)) {
|
|
37928
|
-
s = removeDotSegments(s);
|
|
37967
|
+
s = schemelessReference && s[0] !== "/" ? removeDotSegmentsRelative(s) : removeDotSegments(s);
|
|
37929
37968
|
}
|
|
37930
|
-
if (
|
|
37969
|
+
if (schemelessReference) {
|
|
37931
37970
|
s = serializePathEncoding(s, true);
|
|
37932
37971
|
}
|
|
37933
37972
|
if (authority === undefined && s[0] === "/" && s[1] === "/") {
|
|
@@ -37975,16 +38014,32 @@ var require_fast_uri = __commonJS(function(exports, module) {
|
|
|
37975
38014
|
return hasMalformedPercentEncoding(matches[3]) || host !== undefined && !isIPLiteral(host) && hasMalformedPercentEncoding(host) || hasMalformedPercentEncoding(matches[6]) || hasMalformedPercentEncoding(matches[7]) || hasMalformedPercentEncoding(matches[8]);
|
|
37976
38015
|
}
|
|
37977
38016
|
function canonicalizeHost(parsed, options, schemeHandler, isIP) {
|
|
38017
|
+
if (hasForbiddenDomainHost(parsed, options, schemeHandler, isIP)) {
|
|
38018
|
+
parsed.error = parsed.error || MALFORMED_HOST_ERROR;
|
|
38019
|
+
return true;
|
|
38020
|
+
}
|
|
37978
38021
|
if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport) && parsed.host && !isIPLiteral(parsed.host) && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP === false && nonSimpleDomain(parsed.host)) {
|
|
38022
|
+
let url;
|
|
37979
38023
|
try {
|
|
37980
|
-
|
|
38024
|
+
url = new URL("http://" + parsed.host);
|
|
37981
38025
|
} catch (e) {
|
|
37982
38026
|
parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e;
|
|
37983
38027
|
return true;
|
|
37984
38028
|
}
|
|
38029
|
+
if (!isHostOnlyURL(url)) {
|
|
38030
|
+
parsed.error = parsed.error || MALFORMED_HOST_ERROR;
|
|
38031
|
+
return true;
|
|
38032
|
+
}
|
|
38033
|
+
parsed.host = url.hostname;
|
|
37985
38034
|
}
|
|
37986
38035
|
return false;
|
|
37987
38036
|
}
|
|
38037
|
+
function hasForbiddenDomainHost(parsed, options, schemeHandler, isIP) {
|
|
38038
|
+
return Boolean(parsed.host && isIP === false && !isIPLiteral(parsed.host) && (options.domainHost || schemeHandler && schemeHandler.domainHost) && FORBIDDEN_DOMAIN_HOST_CHARACTER.test(parsed.host));
|
|
38039
|
+
}
|
|
38040
|
+
function isHostOnlyURL(url) {
|
|
38041
|
+
return url.username === "" && url.password === "" && url.port === "" && url.pathname === "/" && url.search === "" && url.hash === "" && url.hostname !== "";
|
|
38042
|
+
}
|
|
37988
38043
|
function parseWithStatus(uri, opts) {
|
|
37989
38044
|
const options = Object.assign({}, opts);
|
|
37990
38045
|
const parsed = {
|
|
@@ -38039,9 +38094,8 @@ var require_fast_uri = __commonJS(function(exports, module) {
|
|
|
38039
38094
|
parsed.query = matches[7];
|
|
38040
38095
|
parsed.fragment = matches[8];
|
|
38041
38096
|
if (parsed.scheme !== undefined) {
|
|
38042
|
-
|
|
38043
|
-
|
|
38044
|
-
parsed.scheme = decodedScheme.toLowerCase();
|
|
38097
|
+
if (VALID_SCHEME.test(parsed.scheme)) {
|
|
38098
|
+
parsed.scheme = parsed.scheme.toLowerCase();
|
|
38045
38099
|
} else {
|
|
38046
38100
|
parsed.error = parsed.error || MALFORMED_SCHEME_ERROR;
|
|
38047
38101
|
malformedScheme = true;
|
|
@@ -38069,7 +38123,7 @@ var require_fast_uri = __commonJS(function(exports, module) {
|
|
|
38069
38123
|
malformedIPLiteral = hasIPLiteralBracket && (!bracketedIPLiteral || ipv6result.error === true);
|
|
38070
38124
|
parsed.host = isIP ? ipv6result.host : ipv6result.host.toLowerCase();
|
|
38071
38125
|
if (malformedIPLiteral) {
|
|
38072
|
-
parsed.error = parsed.error ||
|
|
38126
|
+
parsed.error = parsed.error || MALFORMED_HOST_ERROR;
|
|
38073
38127
|
malformedAuthorityOrPort = true;
|
|
38074
38128
|
}
|
|
38075
38129
|
} else {
|
|
@@ -38097,7 +38151,7 @@ var require_fast_uri = __commonJS(function(exports, module) {
|
|
|
38097
38151
|
if (!isIP) {
|
|
38098
38152
|
host = normalizePercentEncoding(host.toLowerCase());
|
|
38099
38153
|
}
|
|
38100
|
-
parsed.host = reescapeHostDelimiters(host, isIP);
|
|
38154
|
+
parsed.host = isIP || options.scheme !== "null" ? reescapeHostDelimiters(host, isIP) : host;
|
|
38101
38155
|
}
|
|
38102
38156
|
if (!schemeHandler || schemeHandler && !schemeHandler.skipNormalize) {
|
|
38103
38157
|
if (parsed.path) {
|
|
@@ -48308,23 +48362,33 @@ var spec_driven_plan_default = `# [Outcome to deliver]
|
|
|
48308
48362
|
|
|
48309
48363
|
### Big Ideas
|
|
48310
48364
|
|
|
48311
|
-
[
|
|
48365
|
+
[Show changed component relationships with a component diagram, new processes with flowcharts, and new file-based artifacts with trees or suitable visuals. Use Mermaid and fenced text where useful. Keep decisions, responsibilities, invariants, and important failure behavior beside the relevant visual. Draw only the affected parts; keep enough detail to execute without the original chat.]
|
|
48312
48366
|
|
|
48313
48367
|
### Infrastructure Changes
|
|
48314
48368
|
|
|
48315
|
-
[
|
|
48369
|
+
[Actual hosting, services, deployment, networking, or managed-resource changes. New source modules belong in Repo Changes. Write N/A. if no infrastructure changes.]
|
|
48316
48370
|
|
|
48317
48371
|
### Interfaces
|
|
48318
48372
|
|
|
48319
|
-
[
|
|
48373
|
+
[Use compact commands, signatures, code, or pseudocode to show changed public interactions and contracts, including important failure results. Keep implementation actions in the phases.]
|
|
48320
48374
|
|
|
48321
48375
|
### Consequences
|
|
48322
48376
|
|
|
48323
|
-
|
|
48377
|
+
#### Repo Changes
|
|
48378
|
+
|
|
48379
|
+
[Start with a selected file tree: + new, ~ changed, - removed, * largest edits. Include new modules and meaningful structural changes. Use real inspected paths; label proposed files. State when nothing is removed.]
|
|
48380
|
+
|
|
48381
|
+
#### Testing
|
|
48382
|
+
|
|
48383
|
+
[Show a nested outline of end-to-end and integration test scenarios, grouped by feature and behavior. Name the entry point, input/action, and expected result for each scenario. Reuse existing fixtures and suites. Match the scenarios to requirements and phase checks.]
|
|
48384
|
+
|
|
48385
|
+
#### Trade-offs
|
|
48386
|
+
|
|
48387
|
+
[Risks, compatibility, and what is deferred.]
|
|
48324
48388
|
|
|
48325
48389
|
## Phases
|
|
48326
48390
|
|
|
48327
|
-
[
|
|
48391
|
+
[Ordered tasks with file/module targets and enough detail to act. Keep decisions and contracts in the design. Specify the exact application behavior for end-to-end tests and the public module behavior for integration tests. Require clear Arrange/Act/Assert blocks. Use relevant checks and existing helpers. Keep conditional commit and CI gates when expected, including for plan go --commit. Follow the execution workflow's commit/push permissions.]
|
|
48328
48392
|
|
|
48329
48393
|
### Phase 1: [Outcome]
|
|
48330
48394
|
|
|
@@ -48333,8 +48397,9 @@ var spec_driven_plan_default = `# [Outcome to deliver]
|
|
|
48333
48397
|
- [ ] [Task C]
|
|
48334
48398
|
- [ ] [Task D]
|
|
48335
48399
|
- [ ] Validation
|
|
48336
|
-
- [ ]
|
|
48337
|
-
- [ ]
|
|
48400
|
+
- [ ] [Observable application/module contract and the existing or proposed check.]
|
|
48401
|
+
- [ ] [Relevant format, lint, or build checks.]
|
|
48402
|
+
- [ ] [Commit and confirm CI is green, if expected and authorized.]
|
|
48338
48403
|
|
|
48339
48404
|
### Phase 2: [Outcome]
|
|
48340
48405
|
|
|
@@ -48343,8 +48408,9 @@ var spec_driven_plan_default = `# [Outcome to deliver]
|
|
|
48343
48408
|
- [ ] [Task C]
|
|
48344
48409
|
- [ ] [Task D]
|
|
48345
48410
|
- [ ] Validation
|
|
48346
|
-
- [ ]
|
|
48347
|
-
- [ ]
|
|
48411
|
+
- [ ] [Observable application/module contract and the existing or proposed check.]
|
|
48412
|
+
- [ ] [Relevant format, lint, or build checks.]
|
|
48413
|
+
- [ ] [Commit and confirm CI is green, if expected and authorized.]
|
|
48348
48414
|
|
|
48349
48415
|
### Phase 3: [Outcome]
|
|
48350
48416
|
|
|
@@ -48353,8 +48419,9 @@ var spec_driven_plan_default = `# [Outcome to deliver]
|
|
|
48353
48419
|
- [ ] [Task C]
|
|
48354
48420
|
- [ ] [Task D]
|
|
48355
48421
|
- [ ] Validation
|
|
48356
|
-
- [ ]
|
|
48357
|
-
- [ ]
|
|
48422
|
+
- [ ] [Observable application/module contract and the existing or proposed check.]
|
|
48423
|
+
- [ ] [Relevant format, lint, or build checks.]
|
|
48424
|
+
- [ ] [Commit and confirm CI is green, if expected and authorized.]
|
|
48358
48425
|
|
|
48359
48426
|
## References
|
|
48360
48427
|
|
|
@@ -48551,6 +48618,45 @@ var product_requirements_document_default = `# [User outcome or product change]
|
|
|
48551
48618
|
`;
|
|
48552
48619
|
var init_product_requirements_document = () => {};
|
|
48553
48620
|
|
|
48621
|
+
// templates/agents.md
|
|
48622
|
+
var agents_default = `# Repository Guidance
|
|
48623
|
+
|
|
48624
|
+
<!-- Fill the prompts with known repository rules and remove unused prompts. Write terse, imperative bullets in simple English. Use soft wrapping and keep the whole file at or below 500 source lines, including headings and blank lines. Replace contradictory rules, merge duplicates, and prune less important guidance when adding rules. Prefix inferred or uncertain rules with [Proposed]; omit the prefix only for rules the user explicitly asks to add or says must always apply in this repo. Link to maintained docs for details; omit architecture explanations, README repetition, and code snippets. -->
|
|
48625
|
+
|
|
48626
|
+
## Hard Rules
|
|
48627
|
+
|
|
48628
|
+
- Prefer the repository's existing task-runner tasks. See [task definitions](path-to-task-definitions).
|
|
48629
|
+
- [State required scaffolding and repository management tools. Link to their configuration.]
|
|
48630
|
+
- [Add other essential repository constraints.]
|
|
48631
|
+
|
|
48632
|
+
## Organization
|
|
48633
|
+
|
|
48634
|
+
### Repository layout
|
|
48635
|
+
|
|
48636
|
+
- [State directory layout and file naming conventions. Link to maintained docs for details.]
|
|
48637
|
+
|
|
48638
|
+
### File layout
|
|
48639
|
+
|
|
48640
|
+
- [State prescribed section headers, code ordering, and grouping within each file type.]
|
|
48641
|
+
- [Retain terse rules for import, type, API, hook, and helper placement.]
|
|
48642
|
+
|
|
48643
|
+
## Style
|
|
48644
|
+
|
|
48645
|
+
### Languages
|
|
48646
|
+
|
|
48647
|
+
- [Replace this heading with each language used here and add its rules.]
|
|
48648
|
+
|
|
48649
|
+
### Frameworks
|
|
48650
|
+
|
|
48651
|
+
- [Replace this heading with each framework that needs specific rules.]
|
|
48652
|
+
|
|
48653
|
+
## Testing
|
|
48654
|
+
|
|
48655
|
+
- [State where unit, integration, and end-to-end tests belong.]
|
|
48656
|
+
- [State test naming, structure, shared helper, and validation rules. Link to task definitions.]
|
|
48657
|
+
`;
|
|
48658
|
+
var init_agents = () => {};
|
|
48659
|
+
|
|
48554
48660
|
// templates/code-review.md
|
|
48555
48661
|
var code_review_default = `# Review: [change or pull request]
|
|
48556
48662
|
|
|
@@ -48564,31 +48670,35 @@ var code_review_default = `# Review: [change or pull request]
|
|
|
48564
48670
|
|
|
48565
48671
|
## Readability
|
|
48566
48672
|
|
|
48567
|
-
[
|
|
48673
|
+
[Do names and code structure show what happens? Compare names with behavior, side effects, and callers. Follow language conventions and established codebase patterns. Flag misleading names and flows that are hard to follow.]
|
|
48568
48674
|
|
|
48569
48675
|
## Semantic Organization
|
|
48570
48676
|
|
|
48571
|
-
[
|
|
48677
|
+
[Do modules have clear roles and useful public APIs? Identify mixed responsibilities and unclear ownership. Refactor or simplify at the scope needed to make the change understandable, including across files. Keep unrelated work out of scope.]
|
|
48572
48678
|
|
|
48573
48679
|
## Maintainability
|
|
48574
48680
|
|
|
48575
|
-
[
|
|
48681
|
+
[Does the change follow established codebase patterns and applicable AGENTS.md rules? Check new fields, types, helpers, and dependencies against existing code and callers. Reuse suitable code and remove needless complexity.]
|
|
48576
48682
|
|
|
48577
48683
|
## Reliability/Testing
|
|
48578
48684
|
|
|
48579
|
-
[
|
|
48685
|
+
[Prioritize end-to-end application behavior, then module behavior through public APIs, then focused unit behavior with public APIs first. Name missing observable contracts or regressions; do not demand coverage of every function or a percentage. Check semantic grouping, clear Arrange/Act/Assert blocks, fixtures, and existing shared helpers. Flag walls of repeated setup or interleaved actions/assertions that hide the behavior. Report executed checks and limits honestly.]
|
|
48686
|
+
|
|
48687
|
+
## Repository Guidance
|
|
48688
|
+
|
|
48689
|
+
[List root/applicable nested AGENTS.md paths and material deviations in changed files. Cite actual rules, including code/test organization and helper placement where prescribed. If none applies or guidance could not be read, say so. Keep this assessment brief.]
|
|
48580
48690
|
|
|
48581
48691
|
## Performance
|
|
48582
48692
|
|
|
48583
|
-
[
|
|
48693
|
+
[Report material regressions with evidence. Avoid nitpicks and minor optimizations. Deepen the audit for an explicit performance focus. Otherwise mark a deeper audit SKIPPED with a short reason.]
|
|
48584
48694
|
|
|
48585
48695
|
## Security
|
|
48586
48696
|
|
|
48587
|
-
[
|
|
48697
|
+
[Report clear security defects, including exposed secrets or broken authorization/trust boundaries. Deepen the audit for an explicit security focus; avoid unrelated hardening requests. State SKIPPED with a short reason when a deeper audit is not warranted.]
|
|
48588
48698
|
|
|
48589
48699
|
## Change Requests
|
|
48590
48700
|
|
|
48591
|
-
|
|
48701
|
+
Use short comments in simple English. State the problem and its effect directly. Phrase recommendations as suggestions with \u201CLet's\u201D, \u201CI suggest\u201D, or \u201CI recommend\u201D, usually in one to three sentences total. Use the scope that fixes the problem, including a refactor when needed. Group repeated instances of one cause. Omit nitpicks, minor optimizations, and speculative findings. End each generated request and response with its model attribution quote. Preserve human text unchanged.
|
|
48592
48702
|
|
|
48593
48703
|
### [file:line \u2014 request]
|
|
48594
48704
|
|
|
@@ -48603,13 +48713,13 @@ Track actionable inline requests with one entry per request. End each generated
|
|
|
48603
48713
|
|
|
48604
48714
|
## Findings
|
|
48605
48715
|
|
|
48606
|
-
| Severity
|
|
48607
|
-
|
|
|
48608
|
-
| [blocking/consider/note] | [file:line] | [reproducible observation] | [concrete action]
|
|
48716
|
+
| Severity | Location | Evidence and impact | Required change or question |
|
|
48717
|
+
| --- | --- | --- | --- |
|
|
48718
|
+
| [blocking/consider/note] | [file:line] | [reproducible observation] | [concrete action] |
|
|
48609
48719
|
|
|
48610
48720
|
## Overall Assessment
|
|
48611
48721
|
|
|
48612
|
-
[Recommendation and reasons; distinguish findings from preferences.]
|
|
48722
|
+
[Recommendation and reasons; distinguish material findings from preferences. A clean review may have no change requests. Keep section assessments concise.]
|
|
48613
48723
|
|
|
48614
48724
|
## Verification and Limits
|
|
48615
48725
|
|
|
@@ -48726,11 +48836,11 @@ var flow_definition_default = `# {{name}}
|
|
|
48726
48836
|
|
|
48727
48837
|
## Flags
|
|
48728
48838
|
|
|
48729
|
-
| Flag
|
|
48730
|
-
|
|
|
48731
|
-
| --local
|
|
48732
|
-
| --commit
|
|
48733
|
-
| --remote-review | false
|
|
48839
|
+
| Flag | Default | Capability | Effect |
|
|
48840
|
+
| --- | --- | --- | --- |
|
|
48841
|
+
| --local | false | local | Omit steps with \`unless: --local\`; never forward to every skill. |
|
|
48842
|
+
| --commit | false | local-commit | Permit only explicitly commit-capable selected steps to make scoped local commits. |
|
|
48843
|
+
| --remote-review | false | remote-review | Opt in to remote review with independent forge authorization. |
|
|
48734
48844
|
|
|
48735
48845
|
## Steps
|
|
48736
48846
|
|
|
@@ -48922,11 +49032,11 @@ base_commit: '{{full SHA of origin/main at branch creation}}'
|
|
|
48922
49032
|
|
|
48923
49033
|
{{Tool versions, dependency installation, environment inputs, and required services.}}
|
|
48924
49034
|
|
|
48925
|
-
| Task
|
|
48926
|
-
|
|
|
48927
|
-
| Build
|
|
48928
|
-
| Test
|
|
48929
|
-
| Run {{approach}} | \`mise run {{run-task}}\`
|
|
49035
|
+
| Task | Command | Purpose | Observed result |
|
|
49036
|
+
| --- | --- | --- | --- |
|
|
49037
|
+
| Build | \`mise run {{build-task}}\` | {{Build the experiment}} | {{Actual result or pending}} |
|
|
49038
|
+
| Test | \`mise run {{test-task}}\` | {{Validate the experimental behavior}} | {{Actual result or pending}} |
|
|
49039
|
+
| Run {{approach}} | \`mise run {{run-task}}\` | {{Run this prototype; repeat for every approach}} | {{Actual result or pending}} |
|
|
48930
49040
|
|
|
48931
49041
|
{{Explain any genuinely inapplicable build/test task and its alternative validation. Identify unrun checks explicitly.}}
|
|
48932
49042
|
|
|
@@ -48979,15 +49089,77 @@ Project owners need to invite teammates without sharing credentials. Recipients
|
|
|
48979
49089
|
|
|
48980
49090
|
### Big Ideas
|
|
48981
49091
|
|
|
48982
|
-
|
|
49092
|
+
Keep invitation policy in the service and transport concerns in the handler. Store a single-use token hash, project, recipient, and expiry. Use the existing mail queue; no new service is needed.
|
|
49093
|
+
|
|
49094
|
+
\`\`\`mermaid
|
|
49095
|
+
flowchart LR
|
|
49096
|
+
HTTP["HTTP handler"] --> Service["Invitation service<br/>eligibility and acceptance"]
|
|
49097
|
+
Service --> Store["Invitation store<br/>atomic membership + consumption"]
|
|
49098
|
+
Service --> Queue["Existing mail queue"]
|
|
49099
|
+
\`\`\`
|
|
49100
|
+
|
|
49101
|
+
Acceptance adds membership and consumes the invitation in one transaction. Invalid acceptance leaves both unchanged. Queue delivery after saving, so an email outage cannot lose an invitation.
|
|
49102
|
+
|
|
49103
|
+
\`\`\`mermaid
|
|
49104
|
+
flowchart TD
|
|
49105
|
+
Accept["Recipient accepts"] --> Check{"Correct recipient,<br/>unused and unexpired?"}
|
|
49106
|
+
Check -->|"no"| Reject["Reject; leave state unchanged"]
|
|
49107
|
+
Check -->|"yes"| Save["Create membership + consume token<br/>in one transaction"]
|
|
49108
|
+
Save --> Result["Return membership"]
|
|
49109
|
+
\`\`\`
|
|
49110
|
+
|
|
49111
|
+
### Infrastructure Changes
|
|
49112
|
+
|
|
49113
|
+
N/A.
|
|
48983
49114
|
|
|
48984
49115
|
### Interfaces
|
|
48985
49116
|
|
|
48986
|
-
|
|
49117
|
+
\`\`\`text
|
|
49118
|
+
POST /projects/:id/invitations -> pending invitation (owner only)
|
|
49119
|
+
POST /invitations/:token/accept -> membership or safe rejection
|
|
49120
|
+
|
|
49121
|
+
createInvitation(project, email) -> save token hash, then queue delivery
|
|
49122
|
+
acceptInvitation(token, recipient) -> validate, then atomically consume + join
|
|
49123
|
+
\`\`\`
|
|
49124
|
+
|
|
49125
|
+
Never log raw tokens. Apply the existing request rate limit to creation; do not disclose another recipient's account details.
|
|
48987
49126
|
|
|
48988
49127
|
### Consequences
|
|
48989
49128
|
|
|
48990
|
-
|
|
49129
|
+
#### Repo Changes
|
|
49130
|
+
|
|
49131
|
+
Illustrative paths: \`+\` new, \`~\` changed, \`*\` largest edits. No files are removed.
|
|
49132
|
+
|
|
49133
|
+
\`\`\`text
|
|
49134
|
+
src/
|
|
49135
|
+
\u251C\u2500\u2500 invitations/
|
|
49136
|
+
\u2502 \u251C\u2500\u2500 store.ts + persistence and transaction
|
|
49137
|
+
\u2502 \u251C\u2500\u2500 service.ts + * invitation policy
|
|
49138
|
+
\u2502 \u2514\u2500\u2500 service.spec.ts + public API with real test store
|
|
49139
|
+
\u2514\u2500\u2500 http/
|
|
49140
|
+
\u251C\u2500\u2500 routes.ts ~ register invitation handlers
|
|
49141
|
+
\u251C\u2500\u2500 invitations.ts + transport and authorization
|
|
49142
|
+
\u2514\u2500\u2500 invitations.spec.ts + application request scenarios
|
|
49143
|
+
\`\`\`
|
|
49144
|
+
|
|
49145
|
+
#### Testing
|
|
49146
|
+
|
|
49147
|
+
\`\`\`text
|
|
49148
|
+
end-to-end/
|
|
49149
|
+
\u2514\u2500\u2500 invitation acceptance
|
|
49150
|
+
\u251C\u2500\u2500 owner sends invite -> recipient accepts -> membership appears
|
|
49151
|
+
\u2514\u2500\u2500 wrong recipient, expired, or used token -> rejection; state unchanged
|
|
49152
|
+
integration/
|
|
49153
|
+
\u2514\u2500\u2500 invitation service
|
|
49154
|
+
\u251C\u2500\u2500 failed transaction -> membership and token changes roll back
|
|
49155
|
+
\u2514\u2500\u2500 email failure -> saved invitation stays pending for retry
|
|
49156
|
+
\`\`\`
|
|
49157
|
+
|
|
49158
|
+
Use request scenarios in \`src/http/invitations.spec.ts\` and service scenarios in \`src/invitations/service.spec.ts\`. Reuse fixtures with a real test store.
|
|
49159
|
+
|
|
49160
|
+
#### Trade-offs
|
|
49161
|
+
|
|
49162
|
+
Email delivery can fail after persistence. Use existing queue retries and keep the invitation pending. Resend, cancellation, and bulk invitations are deferred.
|
|
48991
49163
|
|
|
48992
49164
|
## Phases
|
|
48993
49165
|
|
|
@@ -48995,19 +49167,20 @@ Email delivery can fail after persistence, so queue retries and show a pending i
|
|
|
48995
49167
|
|
|
48996
49168
|
- [ ] Add a unique token hash, recipient email, project ID, expiry, and consumed timestamp to invitation storage in \`src/invitations/store.ts\`; never log raw tokens.
|
|
48997
49169
|
- [ ] Implement transactional membership creation and token consumption in \`src/invitations/service.ts\`; reject expired, used, or wrong-recipient tokens without a membership change.
|
|
48998
|
-
- [ ]
|
|
49170
|
+
- [ ] Exercise \`createInvitation\` and \`acceptInvitation\` through the service public API with the real test store in \`src/invitations/service.spec.ts\`. Verify rejection leaves membership and token state unchanged, including a failed transaction.
|
|
48999
49171
|
- [ ] Validation
|
|
49000
|
-
- [ ] Run
|
|
49001
|
-
- [ ]
|
|
49172
|
+
- [ ] Run the service contract scenarios; use grouped fixtures and clear Arrange/Act/Assert blocks.
|
|
49173
|
+
- [ ] Run relevant lint and format checks.
|
|
49002
49174
|
|
|
49003
49175
|
### Phase 2: Expose and deliver invitations
|
|
49004
49176
|
|
|
49005
49177
|
- [ ] Add owner creation and recipient acceptance handlers in \`src/http/invitations.ts\` with safe error responses and creation rate limits.
|
|
49006
49178
|
- [ ] Queue delivery after persistence, retry transient mail failures, and expose the pending state to owners.
|
|
49007
|
-
- [ ]
|
|
49179
|
+
- [ ] In \`src/http/invitations.spec.ts\`, send an owner invitation, accept as its recipient, and read back membership. Verify wrong-recipient, expired, and used-token requests leave state unchanged. Verify an email failure keeps the saved invitation pending.
|
|
49008
49180
|
- [ ] Validation
|
|
49009
|
-
- [ ] Run
|
|
49010
|
-
- [ ]
|
|
49181
|
+
- [ ] Run the end-to-end acceptance/rejection scenarios, then the service integration scenarios listed above.
|
|
49182
|
+
- [ ] Run relevant lint and format checks. This example grants no commit or push permission.
|
|
49183
|
+
- [ ] If execution authorizes commits and expects CI, commit the changes and confirm CI is green for that commit.
|
|
49011
49184
|
|
|
49012
49185
|
## References
|
|
49013
49186
|
|
|
@@ -49038,17 +49211,77 @@ Readers should retain access to historical activity while the team changes stora
|
|
|
49038
49211
|
|
|
49039
49212
|
Write to both stores temporarily, backfill older records with stable IDs, compare counts and ordering, then switch readers behind a reversible flag. Keep the legacy reader authoritative until the new store is verified.
|
|
49040
49213
|
|
|
49214
|
+
\`\`\`mermaid
|
|
49215
|
+
flowchart LR
|
|
49216
|
+
Writer["Activity writer"] --> Old["Legacy store"]
|
|
49217
|
+
Writer -->|"new dual write"| New["New store"]
|
|
49218
|
+
Old --> Backfill["Resumable backfill"] --> New
|
|
49219
|
+
Reader["Public history reader"] --> Flag{"Read flag"}
|
|
49220
|
+
Flag -->|"default / rollback"| Old
|
|
49221
|
+
Flag -->|"after parity check"| New
|
|
49222
|
+
\`\`\`
|
|
49223
|
+
|
|
49224
|
+
Stable IDs make retries safe. Track failed writes so comparison cannot silently treat missing data as a successful migration.
|
|
49225
|
+
|
|
49226
|
+
\`\`\`mermaid
|
|
49227
|
+
flowchart TD
|
|
49228
|
+
Provision["Provision store and credentials"] --> Dual["Enable dual writes"]
|
|
49229
|
+
Dual --> Backfill["Backfill in restartable batches"]
|
|
49230
|
+
Backfill --> Compare{"IDs, order,<br/>and cursors match?"}
|
|
49231
|
+
Compare -->|"no"| Repair["Report and repair; keep old reader"] --> Backfill
|
|
49232
|
+
Compare -->|"yes"| Switch["Switch read flag; keep dual writes"]
|
|
49233
|
+
Switch --> Rollback["Rollback: select old reader<br/>without losing new writes"]
|
|
49234
|
+
\`\`\`
|
|
49235
|
+
|
|
49041
49236
|
### Infrastructure Changes
|
|
49042
49237
|
|
|
49043
49238
|
Provision the new activity store and credentials before dual writes. Retain the legacy store and its capacity until the rollback window closes; monitor write failures for each store separately.
|
|
49044
49239
|
|
|
49045
49240
|
### Interfaces
|
|
49046
49241
|
|
|
49047
|
-
|
|
49242
|
+
\`\`\`text
|
|
49243
|
+
writeActivity(event) -> stable ID; retry failed destination writes
|
|
49244
|
+
backfillActivity(projectId, cursor) -> checkpoint + discrepancy report
|
|
49245
|
+
listActivity(projectId, cursor) -> same records, order, and cursor contract
|
|
49246
|
+
readSource = legacy | new # reversible; legacy remains default
|
|
49247
|
+
\`\`\`
|
|
49048
49248
|
|
|
49049
49249
|
### Consequences
|
|
49050
49250
|
|
|
49051
|
-
|
|
49251
|
+
#### Repo Changes
|
|
49252
|
+
|
|
49253
|
+
Illustrative paths: \`+\` new, \`~\` changed, \`*\` largest edits. No files are removed.
|
|
49254
|
+
|
|
49255
|
+
\`\`\`text
|
|
49256
|
+
infra/
|
|
49257
|
+
\u2514\u2500\u2500 activity-store.tf + store, credentials, write-failure metrics
|
|
49258
|
+
src/
|
|
49259
|
+
\u2514\u2500\u2500 activity/
|
|
49260
|
+
\u251C\u2500\u2500 writer.ts ~ * dual writes and retries
|
|
49261
|
+
\u251C\u2500\u2500 backfill.ts + * resumable batches and comparison
|
|
49262
|
+
\u251C\u2500\u2500 reader.ts ~ reversible read source
|
|
49263
|
+
\u251C\u2500\u2500 migration.spec.ts + public API parity and restart scenarios
|
|
49264
|
+
\u2514\u2500\u2500 history.spec.ts ~ application history across cutover/rollback
|
|
49265
|
+
\`\`\`
|
|
49266
|
+
|
|
49267
|
+
#### Testing
|
|
49268
|
+
|
|
49269
|
+
\`\`\`text
|
|
49270
|
+
end-to-end/
|
|
49271
|
+
\u2514\u2500\u2500 activity history
|
|
49272
|
+
\u251C\u2500\u2500 write and page history across cutover -> same records and cursors
|
|
49273
|
+
\u2514\u2500\u2500 write after cutover, then roll back -> no lost events
|
|
49274
|
+
integration/
|
|
49275
|
+
\u2514\u2500\u2500 activity storage
|
|
49276
|
+
\u251C\u2500\u2500 retry a failed destination -> one logical event in each store
|
|
49277
|
+
\u2514\u2500\u2500 interrupt and resume backfill -> same IDs and order; no duplicates
|
|
49278
|
+
\`\`\`
|
|
49279
|
+
|
|
49280
|
+
Use \`src/activity/history.spec.ts\` for application scenarios and \`src/activity/migration.spec.ts\` for public storage APIs with real test stores.
|
|
49281
|
+
|
|
49282
|
+
#### Trade-offs
|
|
49283
|
+
|
|
49284
|
+
Dual writes increase latency and allow partial failure. Record discrepancies and retry failed writes instead of hiding them. Removing the legacy store is outside this plan.
|
|
49052
49285
|
|
|
49053
49286
|
## Phases
|
|
49054
49287
|
|
|
@@ -49056,28 +49289,29 @@ Dual writes increase latency and create a risk of partial failure. Record discre
|
|
|
49056
49289
|
|
|
49057
49290
|
- [ ] Provision the new store and credentials; add write-failure metrics for both stores.
|
|
49058
49291
|
- [ ] Make \`src/activity/writer.ts\` write idempotently to both stores with stable activity IDs while retaining the legacy reader.
|
|
49059
|
-
- [ ]
|
|
49292
|
+
- [ ] Exercise \`writeActivity\` against both test stores in \`src/activity/migration.spec.ts\`: retry a failed destination and verify one logical event in each store.
|
|
49060
49293
|
- [ ] Validation
|
|
49061
|
-
- [ ] Run writer
|
|
49062
|
-
- [ ]
|
|
49294
|
+
- [ ] Run public writer contract scenarios; confirm write-failure metrics are visible.
|
|
49295
|
+
- [ ] Run relevant lint and format checks.
|
|
49063
49296
|
|
|
49064
49297
|
### Phase 2: Backfill and compare
|
|
49065
49298
|
|
|
49066
49299
|
- [ ] Add resumable, idempotent batches in \`src/activity/backfill.ts\` without replacing newer dual-written records.
|
|
49067
49300
|
- [ ] Compare record IDs, counts, and ordering per project; report mismatches before any reader switch.
|
|
49068
|
-
- [ ]
|
|
49301
|
+
- [ ] Extend \`migration.spec.ts\` through \`backfillActivity\` and \`listActivity\`: interrupt and resume a batch, then verify IDs, ordering, and cursors match without duplicate or lost events.
|
|
49069
49302
|
- [ ] Validation
|
|
49070
|
-
- [ ] Run
|
|
49071
|
-
- [ ]
|
|
49303
|
+
- [ ] Run module public-API scenarios and review mismatch reports. Keep fixtures shared and Arrange/Act/Assert visible.
|
|
49304
|
+
- [ ] Run relevant lint and format checks.
|
|
49072
49305
|
|
|
49073
49306
|
### Phase 3: Switch reads with rollback
|
|
49074
49307
|
|
|
49075
49308
|
- [ ] Route \`listActivity(projectId, cursor)\` through a reversible flag in \`src/activity/reader.ts\` and preserve cursor compatibility.
|
|
49076
|
-
- [ ]
|
|
49309
|
+
- [ ] Extend application scenarios in \`src/activity/history.spec.ts\`: write and read paged history, switch stores, write again, and roll back. Assert the same visible history without lost events; do not assert private helper calls.
|
|
49077
49310
|
- [ ] Keep dual writes and monitor parity through the rollback window.
|
|
49078
49311
|
- [ ] Validation
|
|
49079
|
-
- [ ] Run
|
|
49080
|
-
- [ ]
|
|
49312
|
+
- [ ] Run the end-to-end cutover/rollback scenarios, then the storage integration scenarios listed above.
|
|
49313
|
+
- [ ] Run relevant lint and format checks. This example grants no commit or push permission.
|
|
49314
|
+
- [ ] If execution authorizes commits and expects CI, commit the changes and confirm CI is green for that commit.
|
|
49081
49315
|
|
|
49082
49316
|
## References
|
|
49083
49317
|
|
|
@@ -49262,54 +49496,97 @@ Enable for a small cohort, compare completion and feedback, then expand or disab
|
|
|
49262
49496
|
var init_prd_onboarding = () => {};
|
|
49263
49497
|
|
|
49264
49498
|
// templates/examples/review-change.md
|
|
49265
|
-
var review_change_default = `# Review: invitation
|
|
49499
|
+
var review_change_default = `# Review: invitation acceptance
|
|
49500
|
+
|
|
49501
|
+
- **Revision:** illustrative-change-17
|
|
49502
|
+
- **Intent:** Accept a valid invitation and reject an expired one without changing membership.
|
|
49503
|
+
- **Reviewer:** Authored example, not an executed review.
|
|
49266
49504
|
|
|
49267
|
-
|
|
49268
|
-
- **Intent:** Reject expired invitations without creating membership.
|
|
49269
|
-
- **Reviewer:** Example reviewer
|
|
49505
|
+
This is a fictional example. A real review must use its runtime model name in attribution.
|
|
49270
49506
|
|
|
49271
49507
|
## Intent Delivery
|
|
49272
49508
|
|
|
49273
|
-
The acceptance path
|
|
49509
|
+
The change adds an acceptance path. Helper tests do not establish that the request saves membership or leaves it unchanged on rejection.
|
|
49274
49510
|
|
|
49275
49511
|
## Readability
|
|
49276
49512
|
|
|
49277
|
-
|
|
49513
|
+
\`loadInvitation\` consumes the token and creates membership. I suggest \`acceptInvitation\` so callers can see the write.
|
|
49278
49514
|
|
|
49279
49515
|
## Semantic Organization
|
|
49280
49516
|
|
|
49281
|
-
|
|
49517
|
+
The handler maps HTTP to the service. The store owns persistence. No refactor is needed here.
|
|
49282
49518
|
|
|
49283
49519
|
## Maintainability
|
|
49284
49520
|
|
|
49285
|
-
The
|
|
49521
|
+
The change follows the existing store pattern and reuses invitation status.
|
|
49286
49522
|
|
|
49287
49523
|
## Reliability/Testing
|
|
49288
49524
|
|
|
49289
|
-
|
|
49525
|
+
Helper tests do not prove that acceptance saves membership. Let's add application scenarios for acceptance and rejection, then read membership back.
|
|
49526
|
+
|
|
49527
|
+
Repeated setup and nested assertions hide the scenario in \`service.spec.ts:18\`. I suggest a fixture and separate blocks:
|
|
49528
|
+
|
|
49529
|
+
\`\`\`ts
|
|
49530
|
+
describe('acceptInvitation', () => {
|
|
49531
|
+
test('rejects an expired invitation without adding membership', async () => {
|
|
49532
|
+
// Arrange
|
|
49533
|
+
const { service, members, recipient, expiredToken } = await invitationFixture()
|
|
49534
|
+
|
|
49535
|
+
// Act
|
|
49536
|
+
const acceptance = service.acceptInvitation(expiredToken, recipient.id)
|
|
49537
|
+
|
|
49538
|
+
// Assert
|
|
49539
|
+
await expect(acceptance).rejects.toThrow(InvitationExpiredError)
|
|
49540
|
+
expect(await members.list(recipient.id)).toEqual([])
|
|
49541
|
+
})
|
|
49542
|
+
})
|
|
49543
|
+
\`\`\`
|
|
49544
|
+
|
|
49545
|
+
The fixture creates a real test store. I recommend keeping the action and assertions visible and reusing shared setup.
|
|
49546
|
+
|
|
49547
|
+
## Repository Guidance
|
|
49548
|
+
|
|
49549
|
+
The fictional \`AGENTS.md\` requires Arrange/Act/Assert blocks and local helpers at the bottom. I suggest separating the mixed steps in \`service.spec.ts:18\`.
|
|
49290
49550
|
|
|
49291
49551
|
## Performance
|
|
49292
49552
|
|
|
49293
|
-
|
|
49553
|
+
SKIPPED: no concrete performance regression is shown and no deep performance audit was requested.
|
|
49294
49554
|
|
|
49295
49555
|
## Security
|
|
49296
49556
|
|
|
49297
|
-
|
|
49557
|
+
SKIPPED: no concrete security defect is shown; an exhaustive security audit is outside this example's scope.
|
|
49558
|
+
|
|
49559
|
+
## Change Requests
|
|
49560
|
+
|
|
49561
|
+
### src/invitations/service.ts:42 \u2014 Name the state-changing operation
|
|
49562
|
+
|
|
49563
|
+
- **Status:** open
|
|
49564
|
+
- **Request:** \`loadInvitation\` also writes membership. I suggest renaming it to \`acceptInvitation\` and updating callers.
|
|
49565
|
+
|
|
49566
|
+
> Generated via [actual model name from runtime]
|
|
49567
|
+
|
|
49568
|
+
### src/invitations/service.spec.ts:18 \u2014 Expose the scenario and test its public result
|
|
49569
|
+
|
|
49570
|
+
- **Status:** open
|
|
49571
|
+
- **Request:** I suggest the fixture and Arrange/Act/Assert blocks above. Let's add acceptance/rejection scenarios that check saved membership, not just helpers.
|
|
49572
|
+
|
|
49573
|
+
> Generated via [actual model name from runtime]
|
|
49298
49574
|
|
|
49299
49575
|
## Findings
|
|
49300
49576
|
|
|
49301
|
-
| Severity | Location
|
|
49302
|
-
|
|
|
49303
|
-
| consider | \`src/invitations.ts:
|
|
49577
|
+
| Severity | Location | Evidence and impact | Required change or question |
|
|
49578
|
+
| --- | --- | --- | --- |
|
|
49579
|
+
| consider | \`src/invitations/service.ts:42\` | A query-like name hides token consumption and membership creation. | I suggest renaming the operation and callers. |
|
|
49580
|
+
| consider | \`src/invitations/service.spec.ts:18\` | Repeated setup hides the action; helper coverage does not establish saved membership or rejection behavior. | I suggest clear blocks, a fixture, and a public/application scenario. |
|
|
49304
49581
|
|
|
49305
49582
|
## Overall Assessment
|
|
49306
49583
|
|
|
49307
|
-
|
|
49584
|
+
I recommend clearer API naming and acceptance/rejection scenarios. No private-helper coverage or speculative clock-skew request is needed.
|
|
49308
49585
|
|
|
49309
49586
|
## Verification and Limits
|
|
49310
49587
|
|
|
49311
|
-
- **Checks run:**
|
|
49312
|
-
- **Not verified:**
|
|
49588
|
+
- **Checks run:** none; this is an authored example, not a report of passing tests.
|
|
49589
|
+
- **Not verified:** execution, actual repository guidance, real email delivery, and production behavior.
|
|
49313
49590
|
`;
|
|
49314
49591
|
var init_review_change = () => {};
|
|
49315
49592
|
|
|
@@ -49463,12 +49740,13 @@ Add invitation resend and cancellation in a separate change.
|
|
|
49463
49740
|
var init_pull_request_feature = () => {};
|
|
49464
49741
|
|
|
49465
49742
|
// src/templates/source.ts
|
|
49466
|
-
var
|
|
49743
|
+
var contents, catalog, embeddedSource;
|
|
49467
49744
|
var init_source = __esm(() => {
|
|
49468
49745
|
init_spec_driven_plan();
|
|
49469
49746
|
init_software_architecture_design();
|
|
49470
49747
|
init_architecture_decision_record();
|
|
49471
49748
|
init_product_requirements_document();
|
|
49749
|
+
init_agents();
|
|
49472
49750
|
init_code_review();
|
|
49473
49751
|
init_planning_intent();
|
|
49474
49752
|
init_ui_component_architecture();
|
|
@@ -49489,27 +49767,12 @@ var init_source = __esm(() => {
|
|
|
49489
49767
|
init_component_dialog();
|
|
49490
49768
|
init_component_navigation();
|
|
49491
49769
|
init_pull_request_feature();
|
|
49492
|
-
catalog = [
|
|
49493
|
-
{ name: "spec-driven-plan", description: "Spec-driven implementation plan" },
|
|
49494
|
-
{ name: "software-architecture-design", description: "Software architecture design" },
|
|
49495
|
-
{ name: "architecture-decision-record", description: "Architecture decision record" },
|
|
49496
|
-
{ name: "product-requirements-document", description: "Product requirements document" },
|
|
49497
|
-
{ name: "code-review", description: "Thorough code review" },
|
|
49498
|
-
{ name: "planning-intent", description: "Planning intent for a user to fill in" },
|
|
49499
|
-
{ name: "ui-component-architecture", description: "UI component design and implementation" },
|
|
49500
|
-
{ name: "pull-request-description", description: "Draft pull request description" },
|
|
49501
|
-
{ name: "flow-definition", description: "Reusable global Agent Skill flow definition" },
|
|
49502
|
-
{ name: "flow-instance", description: "Frozen repository-local flow run" },
|
|
49503
|
-
{ name: "exploration-summary", description: "Source-grounded technical exploration summary" },
|
|
49504
|
-
{ name: "exploration-research", description: "Technical exploration research notes" },
|
|
49505
|
-
{ name: "exploration-proposal", description: "Optional technical exploration approach" },
|
|
49506
|
-
{ name: "poc-readme", description: "Disposable proof-of-concept branch README" }
|
|
49507
|
-
];
|
|
49508
49770
|
contents = {
|
|
49509
49771
|
"spec-driven-plan.md": spec_driven_plan_default,
|
|
49510
49772
|
"software-architecture-design.md": software_architecture_design_default,
|
|
49511
49773
|
"architecture-decision-record.md": architecture_decision_record_default,
|
|
49512
49774
|
"product-requirements-document.md": product_requirements_document_default,
|
|
49775
|
+
"agents.md": agents_default,
|
|
49513
49776
|
"code-review.md": code_review_default,
|
|
49514
49777
|
"planning-intent.md": planning_intent_default,
|
|
49515
49778
|
"ui-component-architecture.md": ui_component_architecture_default,
|
|
@@ -49531,6 +49794,23 @@ var init_source = __esm(() => {
|
|
|
49531
49794
|
"examples/component-navigation.md": component_navigation_default,
|
|
49532
49795
|
"examples/pull-request-feature.md": pull_request_feature_default
|
|
49533
49796
|
};
|
|
49797
|
+
catalog = [
|
|
49798
|
+
{ name: "spec-driven-plan", description: "Spec-driven implementation plan" },
|
|
49799
|
+
{ name: "software-architecture-design", description: "Software architecture design" },
|
|
49800
|
+
{ name: "architecture-decision-record", description: "Architecture decision record" },
|
|
49801
|
+
{ name: "product-requirements-document", description: "Product requirements document" },
|
|
49802
|
+
{ name: "agents", description: "Concise repository rules for AGENTS.md" },
|
|
49803
|
+
{ name: "code-review", description: "Thorough code review" },
|
|
49804
|
+
{ name: "planning-intent", description: "Planning intent for a user to fill in" },
|
|
49805
|
+
{ name: "ui-component-architecture", description: "UI component design and implementation" },
|
|
49806
|
+
{ name: "pull-request-description", description: "Draft pull request description" },
|
|
49807
|
+
{ name: "flow-definition", description: "Reusable global Agent Skill flow definition" },
|
|
49808
|
+
{ name: "flow-instance", description: "Frozen repository-local flow run" },
|
|
49809
|
+
{ name: "exploration-summary", description: "Source-grounded technical exploration summary" },
|
|
49810
|
+
{ name: "exploration-research", description: "Technical exploration research notes" },
|
|
49811
|
+
{ name: "exploration-proposal", description: "Optional technical exploration approach" },
|
|
49812
|
+
{ name: "poc-readme", description: "Disposable proof-of-concept branch README" }
|
|
49813
|
+
];
|
|
49534
49814
|
embeddedSource = {
|
|
49535
49815
|
catalog,
|
|
49536
49816
|
assets: Object.keys(contents),
|
|
@@ -49896,7 +50176,7 @@ function agentAdapter(name) {
|
|
|
49896
50176
|
return Object.hasOwn(agentAdapters, name) ? agentAdapters[name] : undefined;
|
|
49897
50177
|
}
|
|
49898
50178
|
var agentAdapters, agentNames;
|
|
49899
|
-
var
|
|
50179
|
+
var init_agents2 = __esm(() => {
|
|
49900
50180
|
init_adapter();
|
|
49901
50181
|
init_claude_code();
|
|
49902
50182
|
init_codex();
|
|
@@ -49939,7 +50219,7 @@ async function setupMcpClients(clients, write) {
|
|
|
49939
50219
|
write("Restart or reload the selected client to use the Difflab MCP tools.");
|
|
49940
50220
|
}
|
|
49941
50221
|
var init_setup = __esm(() => {
|
|
49942
|
-
|
|
50222
|
+
init_agents2();
|
|
49943
50223
|
});
|
|
49944
50224
|
|
|
49945
50225
|
// src/mcp/index.ts
|
|
@@ -52086,7 +52366,7 @@ function createProgram(write = console.log, version = "0.1.0", options = {}) {
|
|
|
52086
52366
|
|
|
52087
52367
|
// src/index.ts
|
|
52088
52368
|
init_errors3();
|
|
52089
|
-
var version2 = "0.
|
|
52369
|
+
var version2 = "0.10.0";
|
|
52090
52370
|
try {
|
|
52091
52371
|
await createProgram(console.log, version2).parseAsync(process.argv);
|
|
52092
52372
|
} catch (error) {
|